diff --git a/cli/cmd/deploy.go b/cli/cmd/deploy.go index aef82542f8..ec3d728013 100644 --- a/cli/cmd/deploy.go +++ b/cli/cmd/deploy.go @@ -8,7 +8,7 @@ import ( "context" "github.com/elastic/e2e-testing/cli/config" - "github.com/elastic/e2e-testing/internal/compose" + "github.com/elastic/e2e-testing/internal/deploy" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -63,12 +63,16 @@ func buildDeployServiceCommand(srv string) *cobra.Command { Short: `Deploys a ` + srv + ` service`, Long: `Deploys a ` + srv + ` service, adding it to a running profile, identified by its name`, Run: func(cmd *cobra.Command, args []string) { - serviceManager := compose.NewServiceManager() + serviceManager := deploy.NewServiceManager() env := map[string]string{} env = config.PutServiceEnvironment(env, srv, versionToRun) - err := serviceManager.AddServicesToCompose(context.Background(), deployToProfile, []string{srv}, env) + err := serviceManager.AddServicesToCompose( + context.Background(), + deploy.NewServiceRequest(deployToProfile), + []deploy.ServiceRequest{deploy.NewServiceRequest(srv)}, + env) if err != nil { log.WithFields(log.Fields{ "profile": deployToProfile, @@ -85,12 +89,16 @@ func buildUndeployServiceCommand(srv string) *cobra.Command { Short: `Undeploys a ` + srv + ` service`, Long: `Undeploys a ` + srv + ` service, removing it from a running profile, identified by its name`, Run: func(cmd *cobra.Command, args []string) { - serviceManager := compose.NewServiceManager() + serviceManager := deploy.NewServiceManager() env := map[string]string{} env = config.PutServiceEnvironment(env, srv, versionToRun) - err := serviceManager.RemoveServicesFromCompose(context.Background(), deployToProfile, []string{srv}, env) + err := serviceManager.RemoveServicesFromCompose( + context.Background(), + deploy.NewServiceRequest(deployToProfile), + []deploy.ServiceRequest{deploy.NewServiceRequest(srv)}, + env) if err != nil { log.WithFields(log.Fields{ "profile": deployToProfile, diff --git a/cli/cmd/run.go b/cli/cmd/run.go index 49cde2aeb1..d6c7e437f5 100644 --- a/cli/cmd/run.go +++ b/cli/cmd/run.go @@ -10,7 +10,7 @@ import ( "strings" "github.com/elastic/e2e-testing/cli/config" - "github.com/elastic/e2e-testing/internal/compose" + "github.com/elastic/e2e-testing/internal/deploy" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -64,7 +64,7 @@ func buildRunServiceCommand(srv string) *cobra.Command { Short: `Runs a ` + srv + ` service`, Long: `Runs a ` + srv + ` service, spinning up a Docker container for it and exposing its internal configuration so that you are able to connect to it in an easy manner`, Run: func(cmd *cobra.Command, args []string) { - serviceManager := compose.NewServiceManager() + serviceManager := deploy.NewServiceManager() env := config.PutServiceEnvironment(map[string]string{}, srv, versionToRun) @@ -76,7 +76,8 @@ func buildRunServiceCommand(srv string) *cobra.Command { env[k] = v } - err := serviceManager.RunCompose(context.Background(), false, []string{srv}, env) + err := serviceManager.RunCompose( + context.Background(), false, []deploy.ServiceRequest{deploy.NewServiceRequest(srv)}, env) if err != nil { log.WithFields(log.Fields{ "service": srv, @@ -96,7 +97,7 @@ Example: go run main.go run profile fleet -s elastic-agent:8.0.0-SNAPSHOT `, Run: func(cmd *cobra.Command, args []string) { - serviceManager := compose.NewServiceManager() + serviceManager := deploy.NewServiceManager() env := map[string]string{ "profileVersion": versionToRun, @@ -110,14 +111,15 @@ Example: env[k] = v } - err := serviceManager.RunCompose(context.Background(), true, []string{key}, env) + err := serviceManager.RunCompose( + context.Background(), true, []deploy.ServiceRequest{deploy.NewServiceRequest(key)}, env) if err != nil { log.WithFields(log.Fields{ "profile": key, }).Error("Could not run the profile.") } - composeNames := []string{} + composeNames := []deploy.ServiceRequest{} if len(servicesToRun) > 0 { for _, srv := range servicesToRun { arr := strings.Split(srv, ":") @@ -137,10 +139,10 @@ Example: }).Trace("Adding service") env = config.PutServiceEnvironment(env, image, tag) - composeNames = append(composeNames, image) + composeNames = append(composeNames, deploy.NewServiceRequest(image)) } - err = serviceManager.AddServicesToCompose(context.Background(), key, composeNames, env) + err = serviceManager.AddServicesToCompose(context.Background(), deploy.NewServiceRequest(key), composeNames, env) if err != nil { log.WithFields(log.Fields{ "profile": key, diff --git a/cli/cmd/stop.go b/cli/cmd/stop.go index 984802dc92..974f4f63a0 100644 --- a/cli/cmd/stop.go +++ b/cli/cmd/stop.go @@ -8,7 +8,7 @@ import ( "context" "github.com/elastic/e2e-testing/cli/config" - "github.com/elastic/e2e-testing/internal/compose" + "github.com/elastic/e2e-testing/internal/deploy" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -55,9 +55,10 @@ func buildStopServiceCommand(srv string) *cobra.Command { Short: `Stops a ` + srv + ` service`, Long: `Stops a ` + srv + ` service, stoppping its Docker container`, Run: func(cmd *cobra.Command, args []string) { - serviceManager := compose.NewServiceManager() + serviceManager := deploy.NewServiceManager() - err := serviceManager.StopCompose(context.Background(), false, []string{srv}) + err := serviceManager.StopCompose( + context.Background(), false, []deploy.ServiceRequest{deploy.NewServiceRequest(srv)}) if err != nil { log.WithFields(log.Fields{ "service": srv, @@ -73,9 +74,10 @@ func buildStopProfileCommand(key string, profile config.Profile) *cobra.Command Short: `Stops the ` + profile.Name + ` profile`, Long: `Stops the ` + profile.Name + ` profile, stopping the Services that compound it`, Run: func(cmd *cobra.Command, args []string) { - serviceManager := compose.NewServiceManager() + serviceManager := deploy.NewServiceManager() - err := serviceManager.StopCompose(context.Background(), true, []string{key}) + err := serviceManager.StopCompose( + context.Background(), true, []deploy.ServiceRequest{deploy.NewServiceRequest(key)}) if err != nil { log.WithFields(log.Fields{ "profile": key, diff --git a/cli/config/compose/profiles/fleet/docker-compose.yml b/cli/config/compose/profiles/fleet/docker-compose.yml index 448252ed25..9c31f4f58a 100644 --- a/cli/config/compose/profiles/fleet/docker-compose.yml +++ b/cli/config/compose/profiles/fleet/docker-compose.yml @@ -29,7 +29,7 @@ services: test: "curl -f http://localhost:5601/login | grep kbn-injected-metadata 2>&1 >/dev/null" retries: 600 interval: 1s - image: "docker.elastic.co/${kibanaDockerNamespace:-beats}/kibana:${kibanaVersion:-7.x-SNAPSHOT}" + image: "docker.elastic.co/${kibanaDockerNamespace:-kibana}/kibana:${kibanaVersion:-7.x-SNAPSHOT}" ports: - "5601:5601" volumes: diff --git a/cli/config/compose/services/centos/docker-compose.yml b/cli/config/compose/services/centos/docker-compose.yml deleted file mode 100644 index fa1c2f2162..0000000000 --- a/cli/config/compose/services/centos/docker-compose.yml +++ /dev/null @@ -1,6 +0,0 @@ -version: '2.4' -services: - centos: - image: centos:${centosTag:-7} - container_name: ${centosContainerName} - entrypoint: tail -f /dev/null diff --git a/cli/config/compose/services/debian/docker-compose.yml b/cli/config/compose/services/debian/docker-compose.yml deleted file mode 100644 index cb4491e840..0000000000 --- a/cli/config/compose/services/debian/docker-compose.yml +++ /dev/null @@ -1,6 +0,0 @@ -version: '2.4' -services: - debian: - image: debian:${debianTag:-9} - container_name: ${debianContainerName} - entrypoint: tail -f /dev/null diff --git a/cli/config/compose/services/centos-systemd/docker-compose.yml b/cli/config/compose/services/elastic-agent/centos/docker-compose.yml similarity index 94% rename from cli/config/compose/services/centos-systemd/docker-compose.yml rename to cli/config/compose/services/elastic-agent/centos/docker-compose.yml index 31ca6eb33d..dbe229fc10 100644 --- a/cli/config/compose/services/centos-systemd/docker-compose.yml +++ b/cli/config/compose/services/elastic-agent/centos/docker-compose.yml @@ -1,6 +1,6 @@ version: '2.4' services: - centos-systemd: + elastic-agent: image: centos/systemd:${centos_systemdTag:-latest} container_name: ${centos_systemdContainerName} entrypoint: "/usr/sbin/init" diff --git a/cli/config/compose/services/elastic-agent/docker-compose-cloud.yml b/cli/config/compose/services/elastic-agent/cloud/docker-compose.yml similarity index 100% rename from cli/config/compose/services/elastic-agent/docker-compose-cloud.yml rename to cli/config/compose/services/elastic-agent/cloud/docker-compose.yml diff --git a/cli/config/compose/services/debian-systemd/docker-compose.yml b/cli/config/compose/services/elastic-agent/debian/docker-compose.yml similarity index 94% rename from cli/config/compose/services/debian-systemd/docker-compose.yml rename to cli/config/compose/services/elastic-agent/debian/docker-compose.yml index 1333569887..2c39275ce1 100644 --- a/cli/config/compose/services/debian-systemd/docker-compose.yml +++ b/cli/config/compose/services/elastic-agent/debian/docker-compose.yml @@ -1,6 +1,6 @@ version: '2.4' services: - debian-systemd: + elastic-agent: image: alehaa/debian-systemd:${debian_systemdTag:-stretch} container_name: ${debian_systemdContainerName} entrypoint: "/sbin/init" diff --git a/cli/config/config.go b/cli/config/config.go index a77d574251..569ee254d5 100644 --- a/cli/config/config.go +++ b/cli/config/config.go @@ -73,16 +73,14 @@ func FileExists(configFile string) (bool, error) { // GetComposeFile returns the path of the compose file, looking up the // tool's workdir -func GetComposeFile(isProfile bool, composeName string, composeFileName ...string) (string, error) { - if isProfile || composeFileName == nil || composeFileName[0] == "" { - composeFileName = []string{"docker-compose.yml"} - } +func GetComposeFile(isProfile bool, composeName string) (string, error) { + composeFileName := "docker-compose.yml" serviceType := "services" if isProfile { serviceType = "profiles" } - composeFilePath := path.Join(Op.Workspace, "compose", serviceType, composeName, composeFileName[0]) + composeFilePath := path.Join(Op.Workspace, "compose", serviceType, composeName, composeFileName) found, err := io.Exists(composeFilePath) if found && err == nil { log.WithFields(log.Fields{ diff --git a/e2e/_suites/fleet/features/backend_processes.feature b/e2e/_suites/fleet/features/backend_processes.feature index e4a2b1f710..048413b70f 100644 --- a/e2e/_suites/fleet/features/backend_processes.feature +++ b/e2e/_suites/fleet/features/backend_processes.feature @@ -21,7 +21,7 @@ Examples: Debian @enroll Scenario Outline: Deploying the agent with enroll and then run on rpm and deb - Given a "" agent is deployed to Fleet with "systemd" installer + Given a "" agent is deployed to Fleet When the "elastic-agent" process is in the "started" state on the host Then there are "1" instances of the "filebeat" process in the "started" state And there are "1" instances of the "metricbeat" process in the "started" state diff --git a/e2e/_suites/fleet/features/fleet_mode_agent.feature b/e2e/_suites/fleet/features/fleet_mode_agent.feature index 65c5962460..e5ccf2b2a5 100644 --- a/e2e/_suites/fleet/features/fleet_mode_agent.feature +++ b/e2e/_suites/fleet/features/fleet_mode_agent.feature @@ -21,7 +21,7 @@ Examples: Debian @enroll Scenario Outline: Deploying the agent with enroll and then run on rpm and deb - Given a "" agent is deployed to Fleet with "systemd" installer + Given a "" agent is deployed to Fleet When the "elastic-agent" process is in the "started" state on the host Then the agent is listed in Fleet as "online" And system package dashboards are listed in Fleet diff --git a/e2e/_suites/fleet/fleet.go b/e2e/_suites/fleet/fleet.go index ee4e1bc66a..6156f13a69 100644 --- a/e2e/_suites/fleet/fleet.go +++ b/e2e/_suites/fleet/fleet.go @@ -15,9 +15,7 @@ import ( "github.com/cenkalti/backoff/v4" "github.com/cucumber/godog" "github.com/elastic/e2e-testing/internal/common" - "github.com/elastic/e2e-testing/internal/compose" "github.com/elastic/e2e-testing/internal/deploy" - "github.com/elastic/e2e-testing/internal/docker" "github.com/elastic/e2e-testing/internal/elasticsearch" "github.com/elastic/e2e-testing/internal/installer" "github.com/elastic/e2e-testing/internal/kibana" @@ -30,6 +28,8 @@ import ( const actionADDED = "added" const actionREMOVED = "removed" +var deployedAgentsCount = 0 + // FleetTestSuite represents the scenarios for Fleet-mode type FleetTestSuite struct { // integrations @@ -55,6 +55,8 @@ type FleetTestSuite struct { // afterScenario destroys the state created by a scenario func (fts *FleetTestSuite) afterScenario() { + defer func() { deployedAgentsCount = 0 }() + serviceName := common.ElasticAgentServiceName if !fts.StandAlone { @@ -90,7 +92,18 @@ func (fts *FleetTestSuite) afterScenario() { developerMode := shell.GetEnvBool("DEVELOPER_MODE") if !developerMode { - _ = fts.deployer.Remove([]string{common.FleetProfileName, serviceName}, common.ProfileEnv) + image := "" + if !fts.StandAlone { + agentInstaller := fts.getInstaller() + image = agentInstaller.Image + } + + _ = fts.deployer.Remove( + []deploy.ServiceRequest{ + deploy.NewServiceRequest(common.FleetProfileName), + deploy.NewServiceRequest(serviceName).WithFlavour(image), + }, + common.ProfileEnv) } else { log.WithField("service", serviceName).Info("Because we are running in development mode, the service won't be stopped") } @@ -131,6 +144,7 @@ func (fts *FleetTestSuite) beforeScenario() { } func (fts *FleetTestSuite) contributeSteps(s *godog.ScenarioContext) { + s.Step(`^a "([^"]*)" agent is deployed to Fleet$`, fts.anAgentIsDeployedToFleet) s.Step(`^a "([^"]*)" agent is deployed to Fleet with "([^"]*)" installer$`, fts.anAgentIsDeployedToFleetWithInstaller) s.Step(`^a "([^"]*)" agent "([^"]*)" is deployed to Fleet with "([^"]*)" installer$`, fts.anStaleAgentIsDeployedToFleetWithInstaller) s.Step(`^agent is in version "([^"]*)"$`, fts.agentInVersion) @@ -227,7 +241,7 @@ func (fts *FleetTestSuite) anStaleAgentIsDeployedToFleetWithInstaller(image, ver // prepare installer for stale version if fts.Version != agentVersionBackup { - i := installer.GetElasticAgentInstaller(image, installerType, fts.Version) + i := installer.GetElasticAgentInstaller(image, installerType, fts.Version, deployedAgentsCount) fts.Installers[fmt.Sprintf("%s-%s-%s", image, installerType, version)] = i } @@ -306,7 +320,18 @@ func (fts *FleetTestSuite) agentInVersion(version string) error { return backoff.Retry(agentInVersionFn, exp) } -// supported installers: tar, systemd +// this step infers the installer type from the underlying OS image +// supported images: centos and debian +func (fts *FleetTestSuite) anAgentIsDeployedToFleet(image string) error { + installerType := "rpm" + if image == "debian" { + installerType = "deb" + } + + return fts.anAgentIsDeployedToFleetWithInstallerAndFleetServer(image, installerType) +} + +// supported installers: tar, rpm, deb func (fts *FleetTestSuite) anAgentIsDeployedToFleetWithInstaller(image string, installerType string) error { return fts.anAgentIsDeployedToFleetWithInstallerAndFleetServer(image, installerType) } @@ -317,12 +342,14 @@ func (fts *FleetTestSuite) anAgentIsDeployedToFleetWithInstallerAndFleetServer(i "installer": installerType, }).Trace("Deploying an agent to Fleet with base image using an already bootstrapped Fleet Server") + deployedAgentsCount++ + fts.Image = image fts.InstallerType = installerType agentInstaller := fts.getInstaller() - containerName := fts.getContainerName(agentInstaller, 1) // name of the container + containerName := fts.getContainerName(agentInstaller) // name of the container // enroll the agent with a new token enrollmentKey, err := fts.kibanaClient.CreateEnrollmentAPIKey(fts.Policy) @@ -348,7 +375,7 @@ func (fts *FleetTestSuite) anAgentIsDeployedToFleetWithInstallerAndFleetServer(i } // get container hostname once - hostname, err := docker.GetContainerHostname(containerName) + hostname, err := deploy.GetContainerHostname(containerName) if err != nil { return err } @@ -360,8 +387,8 @@ func (fts *FleetTestSuite) anAgentIsDeployedToFleetWithInstallerAndFleetServer(i // getContainerName returns the current container name for the service: // we are using the Docker client instead of docker-compose because it does not support // returning the output of a command: it simply returns error level -func (fts *FleetTestSuite) getContainerName(i installer.ElasticAgentInstaller, index int) string { - return fmt.Sprintf("%s_%s_%s_%d", i.Profile, i.Image, common.ElasticAgentServiceName, index) +func (fts *FleetTestSuite) getContainerName(i installer.ElasticAgentInstaller) string { + return fmt.Sprintf("%s_%s_%d", i.Profile, common.ElasticAgentServiceName, deployedAgentsCount) } // getServiceName returns the current service name, the one defined at the docker compose @@ -370,13 +397,14 @@ func (fts *FleetTestSuite) getServiceName(i installer.ElasticAgentInstaller) str } func (fts *FleetTestSuite) getInstaller() installer.ElasticAgentInstaller { - key := fmt.Sprintf("%s-%s-%s", fts.Image, fts.InstallerType, fts.Version) + key := fmt.Sprintf("%s-%s-%s-%d", fts.Image, fts.InstallerType, fts.Version, deployedAgentsCount) // check if the agent is already cached if i, exists := fts.Installers[key]; exists { return i } - agentInstaller := installer.GetElasticAgentInstaller(fts.Image, fts.InstallerType, fts.Version) + // setting current index for the installer + agentInstaller := installer.GetElasticAgentInstaller(fts.Image, fts.InstallerType, fts.Version, deployedAgentsCount) // cache the new installer fts.Installers[key] = agentInstaller @@ -391,17 +419,20 @@ func (fts *FleetTestSuite) processStateChangedOnTheHost(process string, state st serviceName := agentInstaller.Service // name of the service + profileService := deploy.NewServiceRequest(profile) + imageService := deploy.NewServiceRequest(common.ElasticAgentServiceName).WithFlavour(agentInstaller.Image) + if state == "started" { - return installer.SystemctlRun(profile, agentInstaller.Image, serviceName, "start") + return installer.SystemctlRun(profileService, imageService, serviceName, "start") } else if state == "restarted" { - err := installer.SystemctlRun(profile, agentInstaller.Image, serviceName, "stop") + err := installer.SystemctlRun(profileService, imageService, serviceName, "stop") if err != nil { return err } utils.Sleep(time.Duration(utils.TimeoutFactor) * 10 * time.Second) - err = installer.SystemctlRun(profile, agentInstaller.Image, serviceName, "start") + err = installer.SystemctlRun(profileService, imageService, serviceName, "start") if err != nil { return err } @@ -427,7 +458,7 @@ func (fts *FleetTestSuite) processStateChangedOnTheHost(process string, state st "process": process, }).Trace("Stopping process on the service") - err := installer.SystemctlRun(profile, agentInstaller.Image, serviceName, "stop") + err := installer.SystemctlRun(profileService, imageService, serviceName, "stop") if err != nil { log.WithFields(log.Fields{ "action": state, @@ -439,7 +470,7 @@ func (fts *FleetTestSuite) processStateChangedOnTheHost(process string, state st return err } - containerName := fts.getContainerName(agentInstaller, 1) + containerName := fts.getContainerName(agentInstaller) return CheckProcessState(fts.deployer, containerName, process, "stopped", 1, utils.TimeoutFactor) } @@ -536,7 +567,7 @@ func theAgentIsListedInFleetWithStatus(desiredStatus string, hostname string) er func (fts *FleetTestSuite) theFileSystemAgentFolderIsEmpty() error { agentInstaller := fts.getInstaller() - containerName := fts.getContainerName(agentInstaller, 1) + containerName := fts.getContainerName(agentInstaller) content, err := agentInstaller.ListElasticAgentWorkingDirContent(containerName) if err != nil { @@ -553,7 +584,7 @@ func (fts *FleetTestSuite) theFileSystemAgentFolderIsEmpty() error { func (fts *FleetTestSuite) theHostIsRestarted() error { agentInstaller := fts.getInstaller() - containerName := fts.getContainerName(agentInstaller, 1) + containerName := fts.getContainerName(agentInstaller) _, err := shell.Execute(context.Background(), ".", "docker", "stop", containerName) if err != nil { log.WithFields(log.Fields{ @@ -981,9 +1012,12 @@ func (fts *FleetTestSuite) theVersionOfThePackageIsInstalled(version string, pac func (fts *FleetTestSuite) anAttemptToEnrollANewAgentFails() error { log.Trace("Enrolling a new agent with an revoked token") + // increase the number of agents + deployedAgentsCount++ + agentInstaller := fts.getInstaller() - containerName := fts.getContainerName(agentInstaller, 2) // name of the new container + containerName := fts.getContainerName(agentInstaller) // name of the new container fleetConfig, err := deployAgentToFleet(agentInstaller, fts.deployer, containerName, fts.CurrentToken) @@ -1136,7 +1170,10 @@ func deployAgentToFleet(agentInstaller installer.ElasticAgentInstaller, deployer // we are setting the container name because Centos service could be reused by any other test suite common.ProfileEnv[envVarsPrefix+"ContainerName"] = containerName - services := []string{profile, service} + agentService := deploy.NewServiceRequest(common.ElasticAgentServiceName).WithFlavour(agentInstaller.Image).WithScale(deployedAgentsCount) + + services := []deploy.ServiceRequest{deploy.NewServiceRequest(profile), agentService} + err := deployer.Add(services, common.ProfileEnv) if err != nil { log.WithFields(log.Fields{ @@ -1150,7 +1187,7 @@ func deployAgentToFleet(agentInstaller installer.ElasticAgentInstaller, deployer targetFile := "/" // copy downloaded agent to the root dir of the container - err = docker.CopyFileToContainer(context.Background(), containerName, agentInstaller.BinaryPath, targetFile, isTar) + err = deploy.CopyFileToContainer(context.Background(), containerName, agentInstaller.BinaryPath, targetFile, isTar) if err != nil { return nil, err } @@ -1217,16 +1254,22 @@ func inputs(integration string) []kibana.Input { } func (fts *FleetTestSuite) getContainerLogs() error { - serviceManager := compose.NewServiceManager() + serviceManager := deploy.NewServiceManager() - profile := common.FleetProfileName + image := "" + if !fts.StandAlone { + agentInstaller := fts.getInstaller() + image = agentInstaller.Image + } + + profile := deploy.NewServiceRequest(common.FleetProfileName) serviceName := common.ElasticAgentServiceName - composes := []string{ - profile, // profile name - serviceName, // agent service + services := []deploy.ServiceRequest{ + profile, // profile name + deploy.NewServiceRequest(serviceName).WithFlavour(image), // agent service } - err := serviceManager.RunCommand(profile, composes, []string{"logs", serviceName}, common.ProfileEnv) + err := serviceManager.RunCommand(profile, services, []string{"logs", serviceName}, common.ProfileEnv) if err != nil { log.WithFields(log.Fields{ "error": err, diff --git a/e2e/_suites/fleet/ingest_manager_test.go b/e2e/_suites/fleet/ingest_manager_test.go index cbfebbb38c..e9df20c969 100644 --- a/e2e/_suites/fleet/ingest_manager_test.go +++ b/e2e/_suites/fleet/ingest_manager_test.go @@ -13,7 +13,6 @@ import ( "github.com/elastic/e2e-testing/cli/config" "github.com/elastic/e2e-testing/internal/common" "github.com/elastic/e2e-testing/internal/deploy" - "github.com/elastic/e2e-testing/internal/docker" "github.com/elastic/e2e-testing/internal/installer" "github.com/elastic/e2e-testing/internal/kibana" "github.com/elastic/e2e-testing/internal/shell" @@ -100,7 +99,7 @@ func InitializeIngestManagerTestSuite(ctx *godog.TestSuiteContext) { "docker.elastic.co/observability-ci/kibana:" + common.KibanaVersion, "docker.elastic.co/observability-ci/kibana-ubi8:" + common.KibanaVersion, } - docker.PullImages(images) + deploy.PullImages(images) } deployer := deploy.New(common.Provider) diff --git a/e2e/_suites/fleet/stand-alone.go b/e2e/_suites/fleet/stand-alone.go index 568660f752..b60d0ff10d 100644 --- a/e2e/_suites/fleet/stand-alone.go +++ b/e2e/_suites/fleet/stand-alone.go @@ -14,7 +14,7 @@ import ( "github.com/cenkalti/backoff/v4" "github.com/elastic/e2e-testing/cli/config" "github.com/elastic/e2e-testing/internal/common" - "github.com/elastic/e2e-testing/internal/docker" + "github.com/elastic/e2e-testing/internal/deploy" "github.com/elastic/e2e-testing/internal/installer" "github.com/elastic/e2e-testing/internal/shell" "github.com/elastic/e2e-testing/internal/utils" @@ -44,7 +44,7 @@ func (fts *FleetTestSuite) aStandaloneAgentIsDeployedWithFleetServerModeOnCloud( } fts.FleetServerPolicy = fleetPolicy volume := path.Join(config.OpDir(), "compose", "services", "elastic-agent", "apm-legacy") - return fts.startStandAloneAgent(image, "docker-compose-cloud.yml", map[string]string{"apmVolume": volume}) + return fts.startStandAloneAgent(image, "cloud", map[string]string{"apmVolume": volume}) } func (fts *FleetTestSuite) thereIsNewDataInTheIndexFromAgent() error { @@ -62,7 +62,11 @@ func (fts *FleetTestSuite) thereIsNewDataInTheIndexFromAgent() error { } func (fts *FleetTestSuite) theDockerContainerIsStopped(serviceName string) error { - err := fts.deployer.Remove([]string{common.FleetProfileName, serviceName}, common.ProfileEnv) + services := []deploy.ServiceRequest{ + deploy.NewServiceRequest(common.FleetProfileName), + deploy.NewServiceRequest(serviceName), + } + err := fts.deployer.Remove(services, common.ProfileEnv) if err != nil { return err } @@ -90,7 +94,7 @@ func (fts *FleetTestSuite) thereIsNoNewDataInTheIndexAfterAgentShutsDown() error return elasticsearch.AssertHitsAreNotPresent(result) } -func (fts *FleetTestSuite) startStandAloneAgent(image string, composeFilename string, env map[string]string) error { +func (fts *FleetTestSuite) startStandAloneAgent(image string, flavour string, env map[string]string) error { fts.StandAlone = true log.Trace("Deploying an agent to Fleet") @@ -102,7 +106,7 @@ func (fts *FleetTestSuite) startStandAloneAgent(image string, composeFilename st // load the docker images that were already: // a. downloaded from the GCP bucket // b. fetched from the local beats binaries - dockerInstaller := installer.GetElasticAgentInstaller("docker", image, common.BeatVersion) + dockerInstaller := installer.GetElasticAgentInstaller("docker", image, common.BeatVersion, deployedAgentsCount) dockerInstaller.PreInstallFn() @@ -126,7 +130,10 @@ func (fts *FleetTestSuite) startStandAloneAgent(image string, composeFilename st common.ProfileEnv[k] = v } - services := []string{common.FleetProfileName, common.ElasticAgentServiceName} + services := []deploy.ServiceRequest{ + deploy.NewServiceRequest(common.FleetProfileName), + deploy.NewServiceRequest(common.ElasticAgentServiceName).WithFlavour(flavour), + } err := fts.deployer.Add(services, common.ProfileEnv) if err != nil { log.Error("Could not deploy the elastic-agent") @@ -134,7 +141,7 @@ func (fts *FleetTestSuite) startStandAloneAgent(image string, composeFilename st } // get container hostname once - hostname, err := docker.GetContainerHostname(containerName) + hostname, err := deploy.GetContainerHostname(containerName) if err != nil { return err } @@ -201,7 +208,7 @@ func (fts *FleetTestSuite) installTestTools(containerName string) error { "containerName": containerName, }).Trace("Installing test tools ") - _, err := docker.ExecCommandIntoContainer(context.Background(), containerName, "root", cmd) + _, err := deploy.ExecCommandIntoContainer(context.Background(), deploy.NewServiceRequest(containerName), "root", cmd) if err != nil { log.WithFields(log.Fields{ "command": cmd, diff --git a/e2e/_suites/fleet/world.go b/e2e/_suites/fleet/world.go index 5737f84430..4e02804303 100644 --- a/e2e/_suites/fleet/world.go +++ b/e2e/_suites/fleet/world.go @@ -35,7 +35,7 @@ func (imts *IngestManagerTestSuite) thereAreInstancesOfTheProcessInTheState(ocur containerName = fmt.Sprintf("%s_%s_%d", profile, common.ElasticAgentServiceName, 1) } else { agentInstaller := imts.Fleet.getInstaller() - containerName = imts.Fleet.getContainerName(agentInstaller, 1) + containerName = imts.Fleet.getContainerName(agentInstaller) } count, err := strconv.Atoi(ocurrences) @@ -85,6 +85,9 @@ func waitForProcess(deployer deploy.Deployment, service string, process string, } retryCount := 1 + // wrap service into a request for the deployer + serviceRequest := deploy.NewServiceRequest(service) + processStatus := func() error { log.WithFields(log.Fields{ "desiredState": desiredState, @@ -95,7 +98,7 @@ func waitForProcess(deployer deploy.Deployment, service string, process string, // pgrep -d: -d, --delimiter specify output delimiter //i.e. "pgrep -d , metricbeat": 483,519 cmds := []string{"pgrep", "-d", ",", process} - output, err := deployer.ExecIn(service, cmds) + output, err := deployer.ExecIn(serviceRequest, cmds) if err != nil { log.WithFields(log.Fields{ "cmds": cmds, @@ -135,7 +138,7 @@ func waitForProcess(deployer deploy.Deployment, service string, process string, for _, pid := range pids { pidStateCmds := []string{"ps", "-q", pid, "-o", "state", "--no-headers"} - pidState, err := deployer.ExecIn(service, pidStateCmds) + pidState, err := deployer.ExecIn(serviceRequest, pidStateCmds) if err != nil { log.WithFields(log.Fields{ "cmds": cmds, diff --git a/e2e/_suites/helm/helm_charts_test.go b/e2e/_suites/helm/helm_charts_test.go index 9f41d649d3..8ab65ce860 100644 --- a/e2e/_suites/helm/helm_charts_test.go +++ b/e2e/_suites/helm/helm_charts_test.go @@ -15,7 +15,7 @@ import ( "github.com/elastic/e2e-testing/cli/config" "github.com/elastic/e2e-testing/e2e/steps" "github.com/elastic/e2e-testing/internal/common" - "github.com/elastic/e2e-testing/internal/compose" + "github.com/elastic/e2e-testing/internal/deploy" "github.com/elastic/e2e-testing/internal/helm" "github.com/elastic/e2e-testing/internal/kubectl" "github.com/elastic/e2e-testing/internal/shell" @@ -660,13 +660,14 @@ func InitializeHelmChartTestSuite(ctx *godog.TestSuiteContext) { elasticAPMEnvironment := shell.GetEnv("ELASTIC_APM_ENVIRONMENT", "ci") if elasticAPMActive && elasticAPMEnvironment == "local" { - serviceManager := compose.NewServiceManager() + serviceManager := deploy.NewServiceManager() env := map[string]string{ "stackVersion": common.StackVersion, } - err := serviceManager.RunCompose(suiteContext, true, []string{"helm"}, env) + err := serviceManager.RunCompose( + suiteContext, true, []deploy.ServiceRequest{deploy.NewServiceRequest("helm")}, env) if err != nil { log.WithFields(log.Fields{ "profile": "metricbeat", @@ -714,8 +715,8 @@ func InitializeHelmChartTestSuite(ctx *godog.TestSuiteContext) { } if elasticAPMActive { - serviceManager := compose.NewServiceManager() - err := serviceManager.StopCompose(suiteContext, true, []string{"helm"}) + serviceManager := deploy.NewServiceManager() + err := serviceManager.StopCompose(suiteContext, true, []deploy.ServiceRequest{deploy.NewServiceRequest("helm")}) if err != nil { log.WithFields(log.Fields{ "profile": "helm", diff --git a/e2e/_suites/kubernetes-autodiscover/autodiscover_test.go b/e2e/_suites/kubernetes-autodiscover/autodiscover_test.go index ef28b4af96..4e1d33c838 100644 --- a/e2e/_suites/kubernetes-autodiscover/autodiscover_test.go +++ b/e2e/_suites/kubernetes-autodiscover/autodiscover_test.go @@ -23,7 +23,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/elastic/e2e-testing/cli/config" - "github.com/elastic/e2e-testing/internal/docker" + "github.com/elastic/e2e-testing/internal/deploy" "github.com/elastic/e2e-testing/internal/kubernetes" "github.com/elastic/e2e-testing/internal/shell" "github.com/elastic/e2e-testing/internal/utils" @@ -128,7 +128,7 @@ func (m *podsManager) configureDockerImage(podName string) error { } // load the TAR file into the docker host as a Docker image - err = docker.LoadImage(imagePath) + err = deploy.LoadImage(imagePath) if err != nil { return err } @@ -136,7 +136,7 @@ func (m *podsManager) configureDockerImage(podName string) error { beatVersion = beatVersion + "-amd64" // tag the image with the proper docker tag, including platform - err = docker.TagImage( + err = deploy.TagImage( "docker.elastic.co/beats/"+podName+":"+defaultBeatVersion, "docker.elastic.co/observability-ci/"+podName+":"+beatVersion, ) diff --git a/e2e/_suites/metricbeat/metricbeat_test.go b/e2e/_suites/metricbeat/metricbeat_test.go index fa99b751f9..3900248c11 100644 --- a/e2e/_suites/metricbeat/metricbeat_test.go +++ b/e2e/_suites/metricbeat/metricbeat_test.go @@ -17,8 +17,7 @@ import ( "github.com/elastic/e2e-testing/cli/config" "github.com/elastic/e2e-testing/e2e/steps" "github.com/elastic/e2e-testing/internal/common" - "github.com/elastic/e2e-testing/internal/compose" - "github.com/elastic/e2e-testing/internal/docker" + "github.com/elastic/e2e-testing/internal/deploy" "github.com/elastic/e2e-testing/internal/elasticsearch" "github.com/elastic/e2e-testing/internal/shell" "github.com/elastic/e2e-testing/internal/utils" @@ -34,7 +33,7 @@ var developerMode = false var elasticAPMActive = false -var serviceManager compose.ServiceManager +var serviceManager deploy.ServiceManager var testSuite MetricbeatTestSuite @@ -58,7 +57,7 @@ func setupSuite() { common.InitVersions() - serviceManager = compose.NewServiceManager() + serviceManager = deploy.NewServiceManager() testSuite = MetricbeatTestSuite{ Query: elasticsearch.Query{}, @@ -123,7 +122,7 @@ func (mts *MetricbeatTestSuite) CleanUp() error { testSuite.currentContext = apm.ContextWithSpan(context.Background(), span) defer span.End() - serviceManager := compose.NewServiceManager() + serviceManager := deploy.NewServiceManager() fn := func(ctx context.Context) { err := elasticsearch.DeleteIndex(ctx, mts.getIndexName()) @@ -140,12 +139,12 @@ func (mts *MetricbeatTestSuite) CleanUp() error { "stackVersion": common.StackVersion, } - services := []string{"metricbeat"} + services := []deploy.ServiceRequest{deploy.NewServiceRequest("metricbeat")} if mts.ServiceName != "" { - services = append(services, mts.ServiceName) + services = append(services, deploy.NewServiceRequest(mts.ServiceName)) } - err := serviceManager.RemoveServicesFromCompose(mts.currentContext, "metricbeat", services, env) + err := serviceManager.RemoveServicesFromCompose(mts.currentContext, deploy.NewServiceRequest("metricbeat"), services, env) if mts.cleanUpTmpFiles { if _, err := os.Stat(mts.configurationFile); err == nil { @@ -229,13 +228,14 @@ func InitializeMetricbeatTestSuite(ctx *godog.TestSuiteContext) { suiteContext = apm.ContextWithSpan(suiteContext, suiteParentSpan) defer suiteParentSpan.End() - serviceManager := compose.NewServiceManager() + serviceManager := deploy.NewServiceManager() env := map[string]string{ "stackVersion": common.StackVersion, } - err := serviceManager.RunCompose(suiteContext, true, []string{"metricbeat"}, env) + err := serviceManager.RunCompose( + suiteContext, true, []deploy.ServiceRequest{deploy.NewServiceRequest("metricbeat")}, env) if err != nil { log.WithFields(log.Fields{ "profile": "metricbeat", @@ -275,8 +275,8 @@ func InitializeMetricbeatTestSuite(ctx *godog.TestSuiteContext) { defer suiteParentSpan.End() if !developerMode { - serviceManager := compose.NewServiceManager() - err := serviceManager.StopCompose(suiteContext, true, []string{"metricbeat"}) + serviceManager := deploy.NewServiceManager() + err := serviceManager.StopCompose(suiteContext, true, []deploy.ServiceRequest{deploy.NewServiceRequest("metricbeat")}) if err != nil { log.WithFields(log.Fields{ "profile": "metricbeat", @@ -378,14 +378,14 @@ func (mts *MetricbeatTestSuite) runMetricbeatService() error { return err } - err = docker.LoadImage(imagePath) + err = deploy.LoadImage(imagePath) if err != nil { return err } mts.Version = mts.Version + "-amd64" - err = docker.TagImage( + err = deploy.TagImage( "docker.elastic.co/beats/metricbeat:"+common.BeatVersionBase, "docker.elastic.co/observability-ci/metricbeat:"+mts.Version, ) @@ -404,7 +404,7 @@ func (mts *MetricbeatTestSuite) runMetricbeatService() error { utils.Sleep(waitForService) - serviceManager := compose.NewServiceManager() + serviceManager := deploy.NewServiceManager() logLevel := log.GetLevel().String() if log.GetLevel() == log.TraceLevel { @@ -425,7 +425,11 @@ func (mts *MetricbeatTestSuite) runMetricbeatService() error { env["metricbeatDockerNamespace"] = utils.GetDockerNamespaceEnvVar("beats") env["metricbeatPlatform"] = "linux/amd64" - err := serviceManager.AddServicesToCompose(testSuite.currentContext, "metricbeat", []string{"metricbeat"}, env) + err := serviceManager.AddServicesToCompose( + testSuite.currentContext, + deploy.NewServiceRequest("metricbeat"), + []deploy.ServiceRequest{deploy.NewServiceRequest("metricbeat")}, + env) if err != nil { log.WithFields(log.Fields{ "error": err, @@ -456,13 +460,13 @@ func (mts *MetricbeatTestSuite) runMetricbeatService() error { } if log.IsLevelEnabled(log.DebugLevel) { - composes := []string{ - "metricbeat", // profile name - "metricbeat", // metricbeat service + services := []deploy.ServiceRequest{ + deploy.NewServiceRequest("metricbeat"), // profile name + deploy.NewServiceRequest("metricbeat"), // metricbeat service } if developerMode { - err = serviceManager.RunCommand("metricbeat", composes, []string{"logs", "metricbeat"}, env) + err = serviceManager.RunCommand(deploy.NewServiceRequest("metricbeat"), services, []string{"logs", "metricbeat"}, env) if err != nil { log.WithFields(log.Fields{ "error": err, @@ -485,7 +489,10 @@ func (mts *MetricbeatTestSuite) serviceIsRunningForMetricbeat(serviceType string } env = config.PutServiceEnvironment(env, serviceType, serviceVersion) - err := serviceManager.AddServicesToCompose(testSuite.currentContext, "metricbeat", []string{serviceType}, env) + err := serviceManager.AddServicesToCompose( + testSuite.currentContext, deploy.NewServiceRequest("metricbeat"), + []deploy.ServiceRequest{deploy.NewServiceRequest(serviceType)}, + env) if err != nil { log.WithFields(log.Fields{ "service": serviceType, @@ -510,7 +517,10 @@ func (mts *MetricbeatTestSuite) serviceVariantIsRunningForMetricbeat( } env = config.PutServiceVariantEnvironment(env, serviceType, serviceVariant, serviceVersion) - err := serviceManager.AddServicesToCompose(testSuite.currentContext, "metricbeat", []string{serviceType}, env) + err := serviceManager.AddServicesToCompose( + testSuite.currentContext, deploy.NewServiceRequest("metricbeat"), + []deploy.ServiceRequest{deploy.NewServiceRequest(serviceType)}, + env) if err != nil { log.WithFields(log.Fields{ "service": serviceType, diff --git a/e2e/steps/befores.go b/e2e/steps/befores.go index d4cb3a2e3b..200c45095b 100644 --- a/e2e/steps/befores.go +++ b/e2e/steps/befores.go @@ -8,24 +8,24 @@ import ( "context" "strings" - "github.com/elastic/e2e-testing/internal/compose" + "github.com/elastic/e2e-testing/internal/deploy" "github.com/elastic/e2e-testing/internal/shell" log "github.com/sirupsen/logrus" ) // AddAPMServicesForInstrumentation adds a Kibana and APM Server instances to the running project func AddAPMServicesForInstrumentation(ctx context.Context, profile string, stackVersion string, needsKibana bool, env map[string]string) { - serviceManager := compose.NewServiceManager() + serviceManager := deploy.NewServiceManager() apmServerURL := shell.GetEnv("APM_SERVER_URL", "") if strings.HasPrefix(apmServerURL, "http://localhost") { - apmServices := []string{ - "apm-server", + apmServices := []deploy.ServiceRequest{ + deploy.NewServiceRequest("apm-server"), } if needsKibana { env["kibanaTag"] = stackVersion - apmServices = append(apmServices, "kibana") + apmServices = append(apmServices, deploy.NewServiceRequest("kibana")) } log.WithFields(log.Fields{ @@ -34,7 +34,7 @@ func AddAPMServicesForInstrumentation(ctx context.Context, profile string, stack }).Info("Starting local APM services for instrumentation") env["apmServerTag"] = stackVersion - err := serviceManager.AddServicesToCompose(ctx, profile, apmServices, env) + err := serviceManager.AddServicesToCompose(ctx, deploy.NewServiceRequest(profile), apmServices, env) if err != nil { log.WithFields(log.Fields{ "error": err, diff --git a/internal/deploy/base.go b/internal/deploy/base.go index 53da675c2f..4500c6ee2c 100644 --- a/internal/deploy/base.go +++ b/internal/deploy/base.go @@ -11,12 +11,12 @@ import ( // Deployment interface for operations dealing with deployments of the bits // required for testing type Deployment interface { - Add(services []string, env map[string]string) error // adds a service to deployment - Bootstrap(waitCB func() error) error // will bootstrap or reuse existing cluster if kubernetes is selected - Destroy() error // Teardown deployment - ExecIn(service string, cmd []string) (string, error) // Execute arbitrary commands in service - Inspect(service string) (*ServiceManifest, error) // inspects service - Remove(services []string, env map[string]string) error // Removes services from deployment + Add(services []ServiceRequest, env map[string]string) error // adds a service to deployment + Bootstrap(waitCB func() error) error // will bootstrap or reuse existing cluster if kubernetes is selected + Destroy() error // Teardown deployment + ExecIn(service ServiceRequest, cmd []string) (string, error) // Execute arbitrary commands in service + Inspect(service ServiceRequest) (*ServiceManifest, error) // inspects service + Remove(services []ServiceRequest, env map[string]string) error // Removes services from deployment } // ServiceManifest information about a service in a deployment @@ -27,6 +27,38 @@ type ServiceManifest struct { Hostname string } +// ServiceRequest represents the service to be created using the provider +type ServiceRequest struct { + Name string + Flavour string // optional, configured using builder method + Scale int // default: 1 +} + +// NewServiceRequest creates a request for a service +func NewServiceRequest(n string) ServiceRequest { + return ServiceRequest{ + Name: n, + Scale: 1, + } +} + +// WithFlavour adds a flavour for the service, resulting in a look-up of the service in the config directory, +// using flavour as a subdir of the service +func (sr ServiceRequest) WithFlavour(f string) ServiceRequest { + sr.Flavour = f + return sr +} + +// WithScale adds the scale index to the service +func (sr ServiceRequest) WithScale(s int) ServiceRequest { + if s < 1 { + s = 1 + } + + sr.Scale = s + return sr +} + // New creates a new deployment func New(provider string) Deployment { if strings.EqualFold(provider, "docker") { diff --git a/internal/compose/compose.go b/internal/deploy/compose.go similarity index 55% rename from internal/compose/compose.go rename to internal/deploy/compose.go index a2f3e2cff5..2a52128c18 100644 --- a/internal/compose/compose.go +++ b/internal/deploy/compose.go @@ -2,7 +2,7 @@ // or more contributor license agreements. Licensed under the Elastic License; // you may not use this file except in compliance with the Elastic License. -package compose +package deploy import ( "context" @@ -19,12 +19,12 @@ import ( // ServiceManager manages lifecycle of a service type ServiceManager interface { - AddServicesToCompose(ctx context.Context, profile string, composeNames []string, env map[string]string, composeFilename ...string) error - ExecCommandInService(profile string, image string, serviceName string, cmds []string, env map[string]string, detach bool) error - RemoveServicesFromCompose(ctx context.Context, profile string, composeNames []string, env map[string]string) error - RunCommand(profile string, composeNames []string, composeArgs []string, env map[string]string) error - RunCompose(ctx context.Context, isProfile bool, composeNames []string, env map[string]string) error - StopCompose(ctx context.Context, isProfile bool, composeNames []string) error + AddServicesToCompose(ctx context.Context, profile ServiceRequest, services []ServiceRequest, env map[string]string) error + ExecCommandInService(profile ServiceRequest, image ServiceRequest, serviceName string, cmds []string, env map[string]string, detach bool) error + RemoveServicesFromCompose(ctx context.Context, profile ServiceRequest, services []ServiceRequest, env map[string]string) error + RunCommand(profile ServiceRequest, services []ServiceRequest, composeArgs []string, env map[string]string) error + RunCompose(ctx context.Context, isProfile bool, services []ServiceRequest, env map[string]string) error + StopCompose(ctx context.Context, isProfile bool, services []ServiceRequest) error } // DockerServiceManager implementation of the service manager interface @@ -37,7 +37,7 @@ func NewServiceManager() ServiceManager { } // AddServicesToCompose adds services to a running docker compose -func (sm *DockerServiceManager) AddServicesToCompose(ctx context.Context, profile string, composeNames []string, env map[string]string, composeFilename ...string) error { +func (sm *DockerServiceManager) AddServicesToCompose(ctx context.Context, profile ServiceRequest, services []ServiceRequest, env map[string]string) error { span, _ := apm.StartSpanOptions(ctx, "Add services to Docker Compose", "docker-compose.services.add", apm.SpanOptions{ Parent: apm.SpanFromContext(ctx).TraceContext(), }) @@ -45,18 +45,30 @@ func (sm *DockerServiceManager) AddServicesToCompose(ctx context.Context, profil log.WithFields(log.Fields{ "profile": profile, - "services": composeNames, + "services": services, }).Trace("Adding services to compose") - newComposeNames := []string{profile} - newComposeNames = append(newComposeNames, composeNames...) + scaleCmds := []string{} + newServices := []ServiceRequest{profile} + for _, srv := range services { + newServices = append(newServices, srv) + if srv.Scale > 1 { + scaleCmds = append(scaleCmds, fmt.Sprintf("%s=%d", srv.Name, srv.Scale)) + } + } - persistedEnv := state.Recover(profile+"-profile", config.Op.Workspace) + persistedEnv := state.Recover(profile.Name+"-profile", config.Op.Workspace) for k, v := range env { persistedEnv[k] = v } - err := executeCompose(true, newComposeNames, []string{"up", "-d"}, persistedEnv, composeFilename...) + cmds := []string{"up", "-d"} + if len(scaleCmds) > 0 { + cmds = append(cmds, "--scale") + cmds = append(cmds, scaleCmds...) + } + + err := executeCompose(true, newServices, cmds, persistedEnv) if err != nil { return err } @@ -65,8 +77,8 @@ func (sm *DockerServiceManager) AddServicesToCompose(ctx context.Context, profil } // ExecCommandInService executes a command in a service from a profile -func (sm *DockerServiceManager) ExecCommandInService(profile string, image string, serviceName string, cmds []string, env map[string]string, detach bool) error { - composes := []string{ +func (sm *DockerServiceManager) ExecCommandInService(profile ServiceRequest, image ServiceRequest, serviceName string, cmds []string, env map[string]string, detach bool) error { + services := []ServiceRequest{ profile, // profile name image, // image for the service } @@ -74,10 +86,11 @@ func (sm *DockerServiceManager) ExecCommandInService(profile string, image strin if detach { composeArgs = append(composeArgs, "-d") } + composeArgs = append(composeArgs, "--index", fmt.Sprintf("%d", image.Scale)) composeArgs = append(composeArgs, serviceName) composeArgs = append(composeArgs, cmds...) - err := sm.RunCommand(profile, composes, composeArgs, env) + err := sm.RunCommand(profile, services, composeArgs, env) if err != nil { log.WithFields(log.Fields{ "command": cmds, @@ -92,7 +105,7 @@ func (sm *DockerServiceManager) ExecCommandInService(profile string, image strin } // RemoveServicesFromCompose removes services from a running docker compose -func (sm *DockerServiceManager) RemoveServicesFromCompose(ctx context.Context, profile string, composeNames []string, env map[string]string) error { +func (sm *DockerServiceManager) RemoveServicesFromCompose(ctx context.Context, profile ServiceRequest, services []ServiceRequest, env map[string]string) error { span, _ := apm.StartSpanOptions(ctx, "Remove services from Docker Compose", "docker-compose.services.remove", apm.SpanOptions{ Parent: apm.SpanFromContext(ctx).TraceContext(), }) @@ -100,33 +113,33 @@ func (sm *DockerServiceManager) RemoveServicesFromCompose(ctx context.Context, p log.WithFields(log.Fields{ "profile": profile, - "services": composeNames, + "services": services, }).Trace("Removing services from compose") - newComposeNames := []string{profile} - newComposeNames = append(newComposeNames, composeNames...) + newServices := []ServiceRequest{profile} + newServices = append(newServices, services...) - persistedEnv := state.Recover(profile+"-profile", config.Op.Workspace) + persistedEnv := state.Recover(profile.Name+"-profile", config.Op.Workspace) for k, v := range env { persistedEnv[k] = v } - for _, composeName := range composeNames { + for _, srv := range services { command := []string{"rm", "-fvs"} - command = append(command, composeName) + command = append(command, srv.Name) - err := executeCompose(true, newComposeNames, command, persistedEnv) + err := executeCompose(true, newServices, command, persistedEnv) if err != nil { log.WithFields(log.Fields{ "command": command, - "service": composeName, + "service": srv, "profile": profile, }).Error("Could not remove service from compose") return err } log.WithFields(log.Fields{ "profile": profile, - "service": composeName, + "service": srv, }).Debug("Service removed from compose") } @@ -134,48 +147,54 @@ func (sm *DockerServiceManager) RemoveServicesFromCompose(ctx context.Context, p } // RunCommand executes a docker-compose command in a running a docker compose -func (sm *DockerServiceManager) RunCommand(profile string, composeNames []string, composeArgs []string, env map[string]string) error { - return executeCompose(true, composeNames, composeArgs, env) +func (sm *DockerServiceManager) RunCommand(profile ServiceRequest, services []ServiceRequest, composeArgs []string, env map[string]string) error { + return executeCompose(true, services, composeArgs, env) } // RunCompose runs a docker compose by its name -func (sm *DockerServiceManager) RunCompose(ctx context.Context, isProfile bool, composeNames []string, env map[string]string) error { +func (sm *DockerServiceManager) RunCompose(ctx context.Context, isProfile bool, services []ServiceRequest, env map[string]string) error { span, _ := apm.StartSpanOptions(ctx, "Starting Docker Compose files", "docker-compose.services.up", apm.SpanOptions{ Parent: apm.SpanFromContext(ctx).TraceContext(), }) defer span.End() - return executeCompose(isProfile, composeNames, []string{"up", "-d"}, env) + return executeCompose(isProfile, services, []string{"up", "-d"}, env) } // StopCompose stops a docker compose by its name -func (sm *DockerServiceManager) StopCompose(ctx context.Context, isProfile bool, composeNames []string) error { +func (sm *DockerServiceManager) StopCompose(ctx context.Context, isProfile bool, services []ServiceRequest) error { span, _ := apm.StartSpanOptions(ctx, "Stopping Docker Compose files", "docker-compose.services.down", apm.SpanOptions{ Parent: apm.SpanFromContext(ctx).TraceContext(), }) defer span.End() - composeFilePaths := make([]string, len(composeNames)) - for i, composeName := range composeNames { + composeFilePaths := make([]string, len(services)) + for i, srv := range services { b := isProfile - if i == 0 && !isProfile && (len(composeName) == 1) { + if i == 0 && !isProfile && (len(services) == 1) { b = true } - composeFilePath, err := config.GetComposeFile(b, composeName) + serviceIncludingFlavour := srv.Name + if srv.Flavour != "" { + // discover the flavour in the subdir + serviceIncludingFlavour = filepath.Join(srv.Name, srv.Flavour) + } + + composeFilePath, err := config.GetComposeFile(b, serviceIncludingFlavour) if err != nil { return fmt.Errorf("Could not get compose file: %s - %v", composeFilePath, err) } composeFilePaths[i] = composeFilePath } - ID := composeNames[0] + "-service" + ID := services[0].Name + "-service" if isProfile { - ID = composeNames[0] + "-profile" + ID = services[0].Name + "-profile" } persistedEnv := state.Recover(ID, config.Op.Workspace) - err := executeCompose(isProfile, composeNames, []string{"down", "--remove-orphans"}, persistedEnv) + err := executeCompose(isProfile, services, []string{"down", "--remove-orphans"}, persistedEnv) if err != nil { return fmt.Errorf("Could not stop compose file: %v - %v", composeFilePaths, err) } @@ -183,28 +202,34 @@ func (sm *DockerServiceManager) StopCompose(ctx context.Context, isProfile bool, log.WithFields(log.Fields{ "composeFilePath": composeFilePaths, - "profile": composeNames[0], + "profile": services[0].Name, }).Trace("Docker compose down.") return nil } -func executeCompose(isProfile bool, composeNames []string, command []string, env map[string]string, composeFilename ...string) error { - composeFilePaths := make([]string, len(composeNames)) - for i, composeName := range composeNames { +func executeCompose(isProfile bool, services []ServiceRequest, command []string, env map[string]string) error { + composeFilePaths := make([]string, len(services)) + for i, srv := range services { b := false if i == 0 && isProfile { b = true } - composeFilePath, err := config.GetComposeFile(b, composeName, composeFilename...) + serviceIncludingFlavour := srv.Name + if srv.Flavour != "" { + // discover the flavour in the subdir + serviceIncludingFlavour = filepath.Join(srv.Name, srv.Flavour) + } + + composeFilePath, err := config.GetComposeFile(b, serviceIncludingFlavour) if err != nil { return fmt.Errorf("Could not get compose file: %s - %v", composeFilePath, err) } composeFilePaths[i] = composeFilePath } - compose := tc.NewLocalDockerCompose(composeFilePaths, composeNames[0]) + compose := tc.NewLocalDockerCompose(composeFilePaths, services[0].Name) execError := compose. WithCommand(command). WithEnv(env). @@ -225,7 +250,7 @@ func executeCompose(isProfile bool, composeNames []string, command []string, env "cmd": command, "composeFilePaths": composeFilePaths, "env": env, - "profile": composeNames[0], + "profile": services[0].Name, }).Debug("Docker compose executed.") return nil diff --git a/internal/deploy/docker.go b/internal/deploy/docker.go index 1eb7ba45e3..27faf4860b 100644 --- a/internal/deploy/docker.go +++ b/internal/deploy/docker.go @@ -9,8 +9,6 @@ import ( "strings" "github.com/elastic/e2e-testing/internal/common" - "github.com/elastic/e2e-testing/internal/compose" - "github.com/elastic/e2e-testing/internal/docker" "github.com/elastic/e2e-testing/internal/utils" log "github.com/sirupsen/logrus" ) @@ -25,15 +23,15 @@ func newDockerDeploy() Deployment { } // Add adds services deployment -func (c *dockerDeploymentManifest) Add(services []string, env map[string]string) error { - serviceManager := compose.NewServiceManager() +func (c *dockerDeploymentManifest) Add(services []ServiceRequest, env map[string]string) error { + serviceManager := NewServiceManager() return serviceManager.AddServicesToCompose(c.Context, services[0], services[1:], env) } // Bootstrap sets up environment with docker compose func (c *dockerDeploymentManifest) Bootstrap(waitCB func() error) error { - serviceManager := compose.NewServiceManager() + serviceManager := NewServiceManager() common.ProfileEnv = map[string]string{ "kibanaVersion": common.KibanaVersion, "stackVersion": common.StackVersion, @@ -45,8 +43,8 @@ func (c *dockerDeploymentManifest) Bootstrap(waitCB func() error) error { common.ProfileEnv["kibanaDockerNamespace"] = "observability-ci" } - profile := common.FleetProfileName - err := serviceManager.RunCompose(c.Context, true, []string{profile}, common.ProfileEnv) + profile := NewServiceRequest(common.FleetProfileName) + err := serviceManager.RunCompose(c.Context, true, []ServiceRequest{profile}, common.ProfileEnv) if err != nil { log.WithFields(log.Fields{ "profile": profile, @@ -62,8 +60,8 @@ func (c *dockerDeploymentManifest) Bootstrap(waitCB func() error) error { // Destroy teardown docker environment func (c *dockerDeploymentManifest) Destroy() error { - serviceManager := compose.NewServiceManager() - err := serviceManager.StopCompose(c.Context, true, []string{common.FleetProfileName}) + serviceManager := NewServiceManager() + err := serviceManager.StopCompose(c.Context, true, []ServiceRequest{NewServiceRequest(common.FleetProfileName)}) if err != nil { log.WithFields(log.Fields{ "error": err, @@ -74,8 +72,8 @@ func (c *dockerDeploymentManifest) Destroy() error { } // ExecIn execute command in service -func (c *dockerDeploymentManifest) ExecIn(service string, cmd []string) (string, error) { - output, err := docker.ExecCommandIntoContainer(c.Context, service, "root", cmd) +func (c *dockerDeploymentManifest) ExecIn(service ServiceRequest, cmd []string) (string, error) { + output, err := ExecCommandIntoContainer(c.Context, service, "root", cmd) if err != nil { return "", err } @@ -83,22 +81,22 @@ func (c *dockerDeploymentManifest) ExecIn(service string, cmd []string) (string, } // Inspect inspects a service -func (c *dockerDeploymentManifest) Inspect(service string) (*ServiceManifest, error) { - inspect, err := docker.InspectContainer(service) +func (c *dockerDeploymentManifest) Inspect(service ServiceRequest) (*ServiceManifest, error) { + inspect, err := InspectContainer(service) if err != nil { return &ServiceManifest{}, err } return &ServiceManifest{ ID: inspect.ID, Name: strings.TrimPrefix(inspect.Name, "/"), - Connection: service, + Connection: service.Name, Hostname: inspect.NetworkSettings.Networks["fleet_default"].Aliases[0], }, nil } // Remove remove services from deployment -func (c *dockerDeploymentManifest) Remove(services []string, env map[string]string) error { - serviceManager := compose.NewServiceManager() +func (c *dockerDeploymentManifest) Remove(services []ServiceRequest, env map[string]string) error { + serviceManager := NewServiceManager() return serviceManager.RemoveServicesFromCompose(c.Context, services[0], services[1:], env) } diff --git a/internal/docker/docker.go b/internal/deploy/docker_client.go similarity index 95% rename from internal/docker/docker.go rename to internal/deploy/docker_client.go index 29c0403e39..88704adc2d 100644 --- a/internal/docker/docker.go +++ b/internal/deploy/docker_client.go @@ -2,7 +2,7 @@ // or more contributor license agreements. Licensed under the Elastic License; // you may not use this file except in compliance with the Elastic License. -package docker +package deploy import ( "archive/tar" @@ -135,17 +135,19 @@ func CopyFileToContainer(ctx context.Context, containerName string, srcPath stri } // ExecCommandIntoContainer executes a command, as a user, into a container -func ExecCommandIntoContainer(ctx context.Context, containerName string, user string, cmd []string) (string, error) { - return ExecCommandIntoContainerWithEnv(ctx, containerName, user, cmd, []string{}) +func ExecCommandIntoContainer(ctx context.Context, container ServiceRequest, user string, cmd []string) (string, error) { + return ExecCommandIntoContainerWithEnv(ctx, container, user, cmd, []string{}) } // ExecCommandIntoContainerWithEnv executes a command, as a user, with env, into a container -func ExecCommandIntoContainerWithEnv(ctx context.Context, containerName string, user string, cmd []string, env []string) (string, error) { +func ExecCommandIntoContainerWithEnv(ctx context.Context, container ServiceRequest, user string, cmd []string, env []string) (string, error) { dockerClient := getDockerClient() detach := false tty := false + containerName := container.Name + log.WithFields(log.Fields{ "container": containerName, "command": cmd, @@ -266,7 +268,7 @@ func GetContainerHostname(containerName string) (string, error) { "containerName": containerName, }).Trace("Retrieving container name from the Docker client") - hostname, err := ExecCommandIntoContainer(context.Background(), containerName, "root", []string{"cat", "/etc/hostname"}) + hostname, err := ExecCommandIntoContainer(context.Background(), NewServiceRequest(containerName), "root", []string{"cat", "/etc/hostname"}) if err != nil { log.WithFields(log.Fields{ "containerName": containerName, @@ -285,13 +287,13 @@ func GetContainerHostname(containerName string) (string, error) { // InspectContainer returns the JSON representation of the inspection of a // Docker container, identified by its name -func InspectContainer(name string) (*types.ContainerJSON, error) { +func InspectContainer(service ServiceRequest) (*types.ContainerJSON, error) { dockerClient := getDockerClient() ctx := context.Background() labelFilters := filters.NewArgs() - labelFilters.Add("name", name) + labelFilters.Add("name", service.Name) containers, err := dockerClient.ContainerList(context.Background(), types.ContainerListOptions{All: true, Filters: labelFilters}) if err != nil { diff --git a/internal/docker/docker_test.go b/internal/deploy/docker_client_test.go similarity index 89% rename from internal/docker/docker_test.go rename to internal/deploy/docker_client_test.go index 2451a8309e..f44d420244 100644 --- a/internal/docker/docker_test.go +++ b/internal/deploy/docker_client_test.go @@ -2,7 +2,7 @@ // or more contributor license agreements. Licensed under the Elastic License; // you may not use this file except in compliance with the Elastic License. -package docker +package deploy import ( "context" @@ -40,7 +40,7 @@ func Test_CopyFile(t *testing.T) { err = CopyFileToContainer(ctx, containerName, src, target, false) assert.Nil(t, err) - output, err := ExecCommandIntoContainer(ctx, containerName, "root", []string{"cat", "/tmp/dockerCopy.txt"}) + output, err := ExecCommandIntoContainer(ctx, NewServiceRequest(containerName), "root", []string{"cat", "/tmp/dockerCopy.txt"}) assert.Nil(t, err) assert.True(t, strings.HasSuffix(output, "OK!"), "File contains the 'OK!' string") }) @@ -76,7 +76,7 @@ func Test_CopyFile(t *testing.T) { err = CopyFileToContainer(ctx, containerName, src, target, true) assert.Nil(t, err) - output, err := ExecCommandIntoContainer(ctx, containerName, "root", []string{"ls", "/project/txtr/kermit.jpg"}) + output, err := ExecCommandIntoContainer(ctx, NewServiceRequest(containerName), "root", []string{"ls", "/project/txtr/kermit.jpg"}) assert.Nil(t, err) assert.True(t, strings.Contains(output, "/project/txtr/kermit.jpg"), "File '/project/txtr/kermit.jpg' should be present") }) diff --git a/internal/deploy/kubernetes.go b/internal/deploy/kubernetes.go index 3b46ab5ab2..2b13196712 100644 --- a/internal/deploy/kubernetes.go +++ b/internal/deploy/kubernetes.go @@ -27,11 +27,11 @@ func newK8sDeploy() Deployment { } // Add adds services deployment -func (c *kubernetesDeploymentManifest) Add(services []string, env map[string]string) error { +func (c *kubernetesDeploymentManifest) Add(services []ServiceRequest, env map[string]string) error { kubectl = cluster.Kubectl().WithNamespace(c.Context, "default") for _, service := range services { - _, err := kubectl.Run(c.Context, "apply", "-k", fmt.Sprintf("../../../cli/config/kubernetes/overlays/%s", service)) + _, err := kubectl.Run(c.Context, "apply", "-k", fmt.Sprintf("../../../cli/config/kubernetes/overlays/%s", service.Name)) if err != nil { return err } @@ -66,9 +66,9 @@ func (c *kubernetesDeploymentManifest) Destroy() error { } // ExecIn execute command in service -func (c *kubernetesDeploymentManifest) ExecIn(service string, cmd []string) (string, error) { +func (c *kubernetesDeploymentManifest) ExecIn(service ServiceRequest, cmd []string) (string, error) { kubectl = cluster.Kubectl().WithNamespace(c.Context, "default") - args := []string{"exec", "deployment/" + service, "--"} + args := []string{"exec", "deployment/" + service.Name, "--"} for _, arg := range cmd { args = append(cmd, arg) } @@ -87,9 +87,9 @@ type kubernetesServiceManifest struct { } // Inspect inspects a service -func (c *kubernetesDeploymentManifest) Inspect(service string) (*ServiceManifest, error) { +func (c *kubernetesDeploymentManifest) Inspect(service ServiceRequest) (*ServiceManifest, error) { kubectl = cluster.Kubectl().WithNamespace(c.Context, "default") - out, err := kubectl.Run(c.Context, "get", "deployment/"+service, "-o", "json") + out, err := kubectl.Run(c.Context, "get", "deployment/"+service.Name, "-o", "json") if err != nil { return &ServiceManifest{}, err } @@ -100,17 +100,17 @@ func (c *kubernetesDeploymentManifest) Inspect(service string) (*ServiceManifest return &ServiceManifest{ ID: inspect.Metadata.ID, Name: strings.TrimPrefix(inspect.Metadata.Name, "/"), - Connection: service, - Hostname: service, + Connection: service.Name, + Hostname: service.Name, }, nil } // Remove remove services from deployment -func (c *kubernetesDeploymentManifest) Remove(services []string, env map[string]string) error { +func (c *kubernetesDeploymentManifest) Remove(services []ServiceRequest, env map[string]string) error { kubectl = cluster.Kubectl().WithNamespace(c.Context, "default") for _, service := range services { - _, err := kubectl.Run(c.Context, "delete", "-k", fmt.Sprintf("../../../cli/config/kubernetes/overlays/%s", service)) + _, err := kubectl.Run(c.Context, "delete", "-k", fmt.Sprintf("../../../cli/config/kubernetes/overlays/%s", service.Name)) if err != nil { return err } diff --git a/internal/installer/base.go b/internal/installer/base.go index 1178c46909..f65a11ae77 100644 --- a/internal/installer/base.go +++ b/internal/installer/base.go @@ -10,8 +10,7 @@ import ( "strings" "github.com/elastic/e2e-testing/internal/common" - "github.com/elastic/e2e-testing/internal/compose" - "github.com/elastic/e2e-testing/internal/docker" + "github.com/elastic/e2e-testing/internal/deploy" "github.com/elastic/e2e-testing/internal/kibana" log "github.com/sirupsen/logrus" ) @@ -38,8 +37,11 @@ type BasePackage struct { // extractPackage depends on the underlying OS, so 'cmds' must contain the specific instructions for the OS func (i *BasePackage) extractPackage(cmds []string) error { - sm := compose.NewServiceManager() - err := sm.ExecCommandInService(i.profile, i.image, i.service, cmds, common.ProfileEnv, false) + sm := deploy.NewServiceManager() + imageService := deploy.NewServiceRequest(common.ElasticAgentServiceName).WithFlavour(i.image) + + err := sm.ExecCommandInService( + deploy.NewServiceRequest(i.profile), imageService, i.service, cmds, common.ProfileEnv, false) if err != nil { log.WithFields(log.Fields{ "command": cmds, @@ -56,11 +58,14 @@ func (i *BasePackage) extractPackage(cmds []string) error { // Postinstall executes operations after installing a DEB package func (i *BasePackage) Postinstall() error { - err := SystemctlRun(i.profile, i.image, i.service, "enable") + profileService := deploy.NewServiceRequest(i.profile) + imageService := deploy.NewServiceRequest(common.ElasticAgentServiceName).WithFlavour(i.image) + + err := SystemctlRun(profileService, imageService, i.service, "enable") if err != nil { return err } - return SystemctlRun(i.profile, i.image, i.service, "start") + return SystemctlRun(profileService, imageService, i.service, "start") } // PrintLogs prints logs for the agent @@ -74,8 +79,11 @@ func (i *BasePackage) PrintLogs(containerName string) error { "cat", i.logFile, } - sm := compose.NewServiceManager() - err = sm.ExecCommandInService(i.profile, i.image, i.service, cmd, common.ProfileEnv, false) + sm := deploy.NewServiceManager() + imageService := deploy.NewServiceRequest(common.ElasticAgentServiceName).WithFlavour(i.image) + + err = sm.ExecCommandInService( + deploy.NewServiceRequest(i.profile), imageService, i.service, cmd, common.ProfileEnv, false) if err != nil { return err } @@ -110,7 +118,7 @@ func getElasticAgentHash(containerName string, commitFile string) (string, error "cat", commitFile, } - fullHash, err := docker.ExecCommandIntoContainer(context.Background(), containerName, "root", cmd) + fullHash, err := deploy.ExecCommandIntoContainer(context.Background(), deploy.NewServiceRequest(containerName), "root", cmd) if err != nil { return "", err } @@ -129,9 +137,9 @@ func getElasticAgentHash(containerName string, commitFile string) (string, error } // SystemctlRun runs systemctl in profile or service -func SystemctlRun(profile string, image string, service string, command string) error { +func SystemctlRun(profile deploy.ServiceRequest, image deploy.ServiceRequest, service string, command string) error { cmd := []string{"systemctl", command, common.ElasticAgentProcessName} - sm := compose.NewServiceManager() + sm := deploy.NewServiceManager() err := sm.ExecCommandInService(profile, image, service, cmd, common.ProfileEnv, false) if err != nil { log.WithFields(log.Fields{ diff --git a/internal/installer/deb.go b/internal/installer/deb.go index 2c708acbc1..08c15b9f8f 100644 --- a/internal/installer/deb.go +++ b/internal/installer/deb.go @@ -6,7 +6,7 @@ package installer import ( "github.com/elastic/e2e-testing/internal/common" - "github.com/elastic/e2e-testing/internal/compose" + "github.com/elastic/e2e-testing/internal/deploy" "github.com/elastic/e2e-testing/internal/kibana" "github.com/elastic/e2e-testing/internal/utils" log "github.com/sirupsen/logrus" @@ -40,14 +40,17 @@ func (i *DEBPackage) InstallCerts() error { return installCertsForDebian(i.profile, i.image, i.service) } func installCertsForDebian(profile string, image string, service string) error { - sm := compose.NewServiceManager() - if err := sm.ExecCommandInService(profile, image, service, []string{"apt-get", "update"}, common.ProfileEnv, false); err != nil { + sm := deploy.NewServiceManager() + serviceProfile := deploy.NewServiceRequest(profile) + serviceImage := deploy.NewServiceRequest(common.ElasticAgentServiceName).WithFlavour(image) + + if err := sm.ExecCommandInService(serviceProfile, serviceImage, service, []string{"apt-get", "update"}, common.ProfileEnv, false); err != nil { return err } - if err := sm.ExecCommandInService(profile, image, service, []string{"apt", "install", "ca-certificates", "-y"}, common.ProfileEnv, false); err != nil { + if err := sm.ExecCommandInService(serviceProfile, serviceImage, service, []string{"apt", "install", "ca-certificates", "-y"}, common.ProfileEnv, false); err != nil { return err } - if err := sm.ExecCommandInService(profile, image, service, []string{"update-ca-certificates", "-f"}, common.ProfileEnv, false); err != nil { + if err := sm.ExecCommandInService(serviceProfile, serviceImage, service, []string{"update-ca-certificates", "-f"}, common.ProfileEnv, false); err != nil { return err } return nil @@ -67,8 +70,7 @@ func (i *DEBPackage) Uninstall() error { // newDebianInstaller returns an instance of the Debian installer for a specific version func newDebianInstaller(image string, tag string, version string) (ElasticAgentInstaller, error) { - image = image + "-systemd" // we want to consume systemd boxes - service := image + service := common.ElasticAgentServiceName profile := common.FleetProfileName // extract the agent in the box, as it's mounted as a volume @@ -91,8 +93,11 @@ func newDebianInstaller(image string, tag string, version string) (ElasticAgentI return ElasticAgentInstaller{}, err } + profileService := deploy.NewServiceRequest(profile) + imageService := deploy.NewServiceRequest(service).WithFlavour("debian") + enrollFn := func(cfg *kibana.FleetConfig) error { - return runElasticAgentCommandEnv(profile, image, service, common.ElasticAgentProcessName, "enroll", cfg.Flags(), map[string]string{}) + return runElasticAgentCommandEnv(profileService, imageService, service, common.ElasticAgentProcessName, "enroll", cfg.Flags(), map[string]string{}) } workingDir := "/var/lib/elastic-agent" diff --git a/internal/installer/docker.go b/internal/installer/docker.go index 3906a10d15..bd6cd1162e 100644 --- a/internal/installer/docker.go +++ b/internal/installer/docker.go @@ -6,7 +6,7 @@ package installer import ( "github.com/elastic/e2e-testing/internal/common" - "github.com/elastic/e2e-testing/internal/docker" + "github.com/elastic/e2e-testing/internal/deploy" "github.com/elastic/e2e-testing/internal/kibana" "github.com/elastic/e2e-testing/internal/utils" log "github.com/sirupsen/logrus" @@ -55,13 +55,13 @@ func (i *DockerPackage) InstallCerts() error { // Preinstall executes operations before installing a Docker package func (i *DockerPackage) Preinstall() error { - err := docker.LoadImage(i.installerPath) + err := deploy.LoadImage(i.installerPath) if err != nil { return err } // we need to tag the loaded image because its tag relates to the target branch - return docker.TagImage( + return deploy.TagImage( "docker.elastic.co/beats/"+i.artifact+":"+common.BeatVersionBase, "docker.elastic.co/observability-ci/"+i.artifact+":"+i.originalVersion+"-amd64", ) @@ -106,7 +106,7 @@ func (i *DockerPackage) WithVersion(version string) *DockerPackage { // newDockerInstaller returns an instance of the Docker installer func newDockerInstaller(ubi8 bool, version string) (ElasticAgentInstaller, error) { - image := "elastic-agent" + image := common.ElasticAgentServiceName service := image profile := common.FleetProfileName diff --git a/internal/installer/elasticagent.go b/internal/installer/elasticagent.go index 45213be826..258e9fb778 100644 --- a/internal/installer/elasticagent.go +++ b/internal/installer/elasticagent.go @@ -9,8 +9,7 @@ import ( "fmt" "github.com/elastic/e2e-testing/internal/common" - "github.com/elastic/e2e-testing/internal/compose" - "github.com/elastic/e2e-testing/internal/docker" + "github.com/elastic/e2e-testing/internal/deploy" "github.com/elastic/e2e-testing/internal/kibana" "github.com/elastic/e2e-testing/internal/utils" log "github.com/sirupsen/logrus" @@ -47,7 +46,7 @@ func (i *ElasticAgentInstaller) ListElasticAgentWorkingDirContent(containerName "ls", "-l", i.workingDir, } - content, err := docker.ExecCommandIntoContainer(context.Background(), containerName, "root", cmd) + content, err := deploy.ExecCommandIntoContainer(context.Background(), deploy.NewServiceRequest(containerName), "root", cmd) if err != nil { return "", err } @@ -62,7 +61,7 @@ func (i *ElasticAgentInstaller) ListElasticAgentWorkingDirContent(containerName } // runElasticAgentCommandEnv runs a command for the elastic-agent -func runElasticAgentCommandEnv(profile string, image string, service string, process string, command string, arguments []string, env map[string]string) error { +func runElasticAgentCommandEnv(profile deploy.ServiceRequest, image deploy.ServiceRequest, service string, process string, command string, arguments []string, env map[string]string) error { cmds := []string{ "timeout", fmt.Sprintf("%dm", utils.TimeoutFactor), process, command, } @@ -72,7 +71,7 @@ func runElasticAgentCommandEnv(profile string, image string, service string, pro common.ProfileEnv[k] = v } - sm := compose.NewServiceManager() + sm := deploy.NewServiceManager() err := sm.ExecCommandInService(profile, image, service, cmds, common.ProfileEnv, false) if err != nil { log.WithFields(log.Fields{ @@ -106,7 +105,7 @@ func downloadAgentBinary(artifactName string, artifact string, version string) ( } // GetElasticAgentInstaller returns an installer from a docker image -func GetElasticAgentInstaller(image string, installerType string, version string) ElasticAgentInstaller { +func GetElasticAgentInstaller(image string, installerType string, version string, index int) ElasticAgentInstaller { log.WithFields(log.Fields{ "image": image, "installer": installerType, @@ -116,12 +115,12 @@ func GetElasticAgentInstaller(image string, installerType string, version string var installer ElasticAgentInstaller var err error if "centos" == image && "tar" == installerType { - installer, err = newTarInstaller("centos", "latest", version) - } else if "centos" == image && "systemd" == installerType { + installer, err = newTarInstaller("centos", "latest", version, index) + } else if "centos" == image && "rpm" == installerType { installer, err = newCentosInstaller("centos", "latest", version) } else if "debian" == image && "tar" == installerType { - installer, err = newTarInstaller("debian", "stretch", version) - } else if "debian" == image && "systemd" == installerType { + installer, err = newTarInstaller("debian", "stretch", version, index) + } else if "debian" == image && "deb" == installerType { installer, err = newDebianInstaller("debian", "stretch", version) } else if "docker" == image && "default" == installerType { installer, err = newDockerInstaller(false, version) diff --git a/internal/installer/rpm.go b/internal/installer/rpm.go index b80c723c18..dc320db349 100644 --- a/internal/installer/rpm.go +++ b/internal/installer/rpm.go @@ -6,7 +6,7 @@ package installer import ( "github.com/elastic/e2e-testing/internal/common" - "github.com/elastic/e2e-testing/internal/compose" + "github.com/elastic/e2e-testing/internal/deploy" "github.com/elastic/e2e-testing/internal/kibana" "github.com/elastic/e2e-testing/internal/utils" log "github.com/sirupsen/logrus" @@ -41,17 +41,20 @@ func (i *RPMPackage) InstallCerts() error { return installCertsForCentos(i.profile, i.image, i.service) } func installCertsForCentos(profile string, image string, service string) error { - sm := compose.NewServiceManager() - if err := sm.ExecCommandInService(profile, image, service, []string{"yum", "check-update"}, common.ProfileEnv, false); err != nil { + sm := deploy.NewServiceManager() + serviceProfile := deploy.NewServiceRequest(profile) + serviceImage := deploy.NewServiceRequest(common.ElasticAgentServiceName).WithFlavour(image) + + if err := sm.ExecCommandInService(serviceProfile, serviceImage, service, []string{"yum", "check-update"}, common.ProfileEnv, false); err != nil { return err } - if err := sm.ExecCommandInService(profile, image, service, []string{"yum", "install", "ca-certificates", "-y"}, common.ProfileEnv, false); err != nil { + if err := sm.ExecCommandInService(serviceProfile, serviceImage, service, []string{"yum", "install", "ca-certificates", "-y"}, common.ProfileEnv, false); err != nil { return err } - if err := sm.ExecCommandInService(profile, image, service, []string{"update-ca-trust", "force-enable"}, common.ProfileEnv, false); err != nil { + if err := sm.ExecCommandInService(serviceProfile, serviceImage, service, []string{"update-ca-trust", "force-enable"}, common.ProfileEnv, false); err != nil { return err } - if err := sm.ExecCommandInService(profile, image, service, []string{"update-ca-trust", "extract"}, common.ProfileEnv, false); err != nil { + if err := sm.ExecCommandInService(serviceProfile, serviceImage, service, []string{"update-ca-trust", "extract"}, common.ProfileEnv, false); err != nil { return err } return nil @@ -71,8 +74,7 @@ func (i *RPMPackage) Uninstall() error { // newCentosInstaller returns an instance of the Centos installer for a specific version func newCentosInstaller(image string, tag string, version string) (ElasticAgentInstaller, error) { - image = image + "-systemd" // we want to consume systemd boxes - service := image + service := common.ElasticAgentServiceName profile := common.FleetProfileName // extract the agent in the box, as it's mounted as a volume @@ -95,8 +97,11 @@ func newCentosInstaller(image string, tag string, version string) (ElasticAgentI return ElasticAgentInstaller{}, err } + profileService := deploy.NewServiceRequest(profile) + imageService := deploy.NewServiceRequest(service).WithFlavour("centos") + enrollFn := func(cfg *kibana.FleetConfig) error { - return runElasticAgentCommandEnv(profile, image, service, common.ElasticAgentProcessName, "enroll", cfg.Flags(), map[string]string{}) + return runElasticAgentCommandEnv(profileService, imageService, service, common.ElasticAgentProcessName, "enroll", cfg.Flags(), map[string]string{}) } workingDir := "/var/lib/elastic-agent" diff --git a/internal/installer/tar.go b/internal/installer/tar.go index b98f5c04ba..b853832b4e 100644 --- a/internal/installer/tar.go +++ b/internal/installer/tar.go @@ -8,7 +8,7 @@ import ( "fmt" "github.com/elastic/e2e-testing/internal/common" - "github.com/elastic/e2e-testing/internal/compose" + "github.com/elastic/e2e-testing/internal/deploy" "github.com/elastic/e2e-testing/internal/kibana" "github.com/elastic/e2e-testing/internal/utils" log "github.com/sirupsen/logrus" @@ -20,6 +20,7 @@ type TARPackage struct { // optional fields arch string artifact string + index int OS string OSFlavour string // at this moment, centos or debian version string @@ -45,7 +46,10 @@ func (i *TARPackage) Install(cfg *kibana.FleetConfig) error { binary := fmt.Sprintf("/elastic-agent/%s", i.artifact) args := cfg.Flags() - err := runElasticAgentCommandEnv(i.profile, i.image, i.service, binary, "install", args, map[string]string{}) + profileService := deploy.NewServiceRequest(i.profile) + imageService := deploy.NewServiceRequest(common.ElasticAgentServiceName).WithFlavour(i.OSFlavour).WithScale(i.index) + + err := runElasticAgentCommandEnv(profileService, imageService, i.service, binary, "install", args, map[string]string{}) if err != nil { return fmt.Errorf("Failed to install the agent with subcommand: %v", err) } @@ -83,9 +87,13 @@ func (i *TARPackage) Preinstall() error { {"rm", "-fr", "/elastic-agent"}, {"mv", fmt.Sprintf("/%s-%s-%s-%s", i.artifact, i.version, i.OS, i.arch), "/elastic-agent"}, } + + profileService := deploy.NewServiceRequest(i.profile) + imageService := deploy.NewServiceRequest(common.ElasticAgentServiceName).WithFlavour(i.OSFlavour).WithScale(i.index) + for _, cmd := range cmds { - sm := compose.NewServiceManager() - err := sm.ExecCommandInService(i.profile, i.image, i.service, cmd, common.ProfileEnv, false) + sm := deploy.NewServiceManager() + err := sm.ExecCommandInService(profileService, imageService, i.service, cmd, common.ProfileEnv, false) if err != nil { log.WithFields(log.Fields{ "command": cmd, @@ -106,7 +114,10 @@ func (i *TARPackage) Preinstall() error { func (i *TARPackage) Uninstall() error { args := []string{"-f"} - return runElasticAgentCommandEnv(i.profile, i.image, i.service, common.ElasticAgentProcessName, "uninstall", args, map[string]string{}) + profileService := deploy.NewServiceRequest(i.profile) + imageService := deploy.NewServiceRequest(common.ElasticAgentServiceName).WithFlavour(i.OSFlavour).WithScale(i.index) + + return runElasticAgentCommandEnv(profileService, imageService, i.service, common.ElasticAgentProcessName, "uninstall", args, map[string]string{}) } // WithArch sets the architecture @@ -121,6 +132,12 @@ func (i *TARPackage) WithArtifact(artifact string) *TARPackage { return i } +// WithIndex sets the index of the agent +func (i *TARPackage) WithIndex(index int) *TARPackage { + i.index = index + return i +} + // WithOS sets the OS func (i *TARPackage) WithOS(OS string) *TARPackage { i.OS = OS @@ -140,10 +157,8 @@ func (i *TARPackage) WithVersion(version string) *TARPackage { } // newTarInstaller returns an instance of the Debian installer for a specific version -func newTarInstaller(image string, tag string, version string) (ElasticAgentInstaller, error) { - dockerImage := image + "-systemd" // we want to consume systemd boxes - - service := dockerImage +func newTarInstaller(image string, tag string, version string, index int) (ElasticAgentInstaller, error) { + service := common.ElasticAgentServiceName profile := common.FleetProfileName // extract the agent in the box, as it's mounted as a volume @@ -174,14 +189,18 @@ func newTarInstaller(image string, tag string, version string) (ElasticAgentInst logFileName := "elastic-agent-json.log" logFile := logsDir + "/" + logFileName + profileService := deploy.NewServiceRequest(profile) + imageService := deploy.NewServiceRequest(service).WithFlavour(image) + enrollFn := func(cfg *kibana.FleetConfig) error { - return runElasticAgentCommandEnv(profile, dockerImage, service, common.ElasticAgentProcessName, "enroll", cfg.Flags(), map[string]string{}) + return runElasticAgentCommandEnv(profileService, imageService, service, common.ElasticAgentProcessName, "enroll", cfg.Flags(), map[string]string{}) } // - installerPackage := NewTARPackage(binaryName, profile, dockerImage, service, commitFile, logFile). + installerPackage := NewTARPackage(binaryName, profile, image, service, commitFile, logFile). WithArch(arch). WithArtifact(artifact). + WithIndex(index). WithOS(os). WithOSFlavour(image). WithVersion(utils.CheckPRVersion(version, common.BeatVersionBase)) // sanitize version @@ -194,7 +213,7 @@ func newTarInstaller(image string, tag string, version string) (ElasticAgentInst artifactVersion: version, BinaryPath: binaryPath, EnrollFn: enrollFn, - Image: dockerImage, + Image: image, InstallFn: installerPackage.Install, InstallCertsFn: installerPackage.InstallCerts, InstallerType: "tar",