Skip to content
This repository was archived by the owner on Sep 17, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b1931c0
chore: capture Fleet's default policy in a stronger manner
mdelapenya Mar 15, 2021
6f54550
chore: support passing the field for is_default policy
mdelapenya Mar 15, 2021
ad0d17f
chore: remove inferred type for array
mdelapenya Mar 15, 2021
e52c8b6
chore: enable fleet server in kibana config
mdelapenya Mar 15, 2021
e8bee02
chore: create fleet config struct
mdelapenya Mar 15, 2021
bb62c4f
chore: refactor enroll command logic to use the new struct
mdelapenya Mar 15, 2021
d8e57ce
chore: check if the fleet-server field exists when retrieving the policy
mdelapenya Mar 15, 2021
6189bff
chore: refactor install to support fleet-server
mdelapenya Mar 16, 2021
d9da8b0
feat: add first scenario for fleet server
mdelapenya Mar 16, 2021
830eb12
chore: add fleet server branch to the CI
mdelapenya Mar 16, 2021
eaa68f2
Merge branch 'master' into 438-fleet-server-scenarios
mdelapenya Mar 18, 2021
bc9e1ff
chore: set Then clause for the scenario
mdelapenya Mar 18, 2021
4ae1019
chore: remove step
mdelapenya Mar 18, 2021
721aa21
fix: define fallback when checking agent status
mdelapenya Mar 22, 2021
f5a9f46
chore: simplify creation of Fleet configs
mdelapenya Mar 24, 2021
eb8f013
fix: forgot to rename variable
mdelapenya Mar 24, 2021
e9edde9
Merge branch 'master' into 438-fleet-server-scenarios
mdelapenya Mar 26, 2021
356750f
WIP
mdelapenya Mar 29, 2021
f2244ad
Merge branch 'master' into 438-fleet-server-scenarios
mdelapenya Mar 29, 2021
7358928
chore: rename scenario
mdelapenya Mar 30, 2021
bcd69ee
Merge branch 'master' into 438-fleet-server-scenarios
mdelapenya Apr 6, 2021
173004f
fix: wrong merge conflicts resolution
mdelapenya Apr 6, 2021
cb57360
chore: support passing environment when running a command in a container
mdelapenya Apr 8, 2021
6865e98
chore: run elastic agent commands passing an env
mdelapenya Apr 8, 2021
6d60a6d
WIP
mdelapenya Apr 8, 2021
04c7fc8
Merge branch 'master' into 438-fleet-server-scenarios
mdelapenya Apr 15, 2021
e5ed65c
chore: separate bootstrapping an agent from connecting to a fleet ser…
mdelapenya Apr 15, 2021
09e4325
fix: use proper fleet-server flags
mdelapenya Apr 15, 2021
491eb17
Merge branch 'master' into 438-fleet-server-scenarios
adam-stokes Apr 16, 2021
a557f88
Merge branch 'master' into 438-fleet-server-scenarios
adam-stokes Apr 19, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .ci/.e2e-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ SUITES:
- name: "Fleet"
pullRequestFilter: " && ~debian"
tags: "fleet_mode_agent"
- name: "Fleet Server"
pullRequestFilter: " && ~debian"
tags: "fleet_server"
- name: "Endpoint Integration"
pullRequestFilter: " && ~debian"
tags: "agent_endpoint_integration"
Expand Down
1 change: 1 addition & 0 deletions e2e/_suites/fleet/configurations/kibana.config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ xpack.fleet.enabled: true
xpack.fleet.registryUrl: http://package-registry:8080
xpack.fleet.agents.enabled: true
xpack.fleet.agents.elasticsearch.host: http://elasticsearch:9200
xpack.fleet.agents.fleetServerEnabled: true
xpack.fleet.agents.kibana.host: http://kibana:5601
xpack.fleet.agents.tlsCheckDisabled: true
19 changes: 19 additions & 0 deletions e2e/_suites/fleet/features/fleet_server.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@fleet_server
Feature: Fleet Server
Scenarios for Fleet Server, where an Elasticseach and a Kibana instances are already provisioned,
so that the Agent is able to communicate with them

@start-fleet-server
Scenario Outline: Deploying the <os> fleet-server agent enables Fleet Server

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is surely because of my lack of understanding of how fleet-server works, but I don't find this phrasing very clear: there is no such a thing as "fleet-server agent", right? There is an Elastic Agent that can start a Fleet Server process: shouldn't this be in the scenario definition?

Since "Fleet" is such a ubiquitous term, it might be worth to be more explicit every time is it used: Is it Fleet API, Fleet UI, etc.
Related: since this spec is meant to be semi-formal, and we put a lot of effort in standarisation elsewhere (linting, etc), It would be good to define components canonically. Eg., always "Elastic Agent", and not a combination of "Elastic Agent", "Agent", "the agent", etc.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I strongly agree with this. 💯

What about sending the refactors in a follow-up? Let's create an issue to standardise the names across Fleet test suite. Maybe @EricDavisX can help with the wording.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to the name, yes. It could be rephrased. Let me add a suggestion.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another note about this pr vs a separate one... we have a new feature file which is helpful, but to me, it is mostly helpful in confirming we have the code right here, with a straightforward usage. We'll need to adapt (now or in a separate PR) all of the:
Given a "" agent is deployed to Fleet with "tar" installer
to
When a "" agent is deployed to Fleet with "tar" installer in fleet-server mode
...because any non-Fleet server usage (in Fleet) will not be supported. The only non-Fleet-Server usage will be as a stand-alone Agent mode.

So, all the following feature files will need review and update, at least in some way (listing them out explicitly):
e2e/_suites/fleet/features/agent_endpoint_integration.feature
e2e/_suites/fleet/features/backend_processes.feature
e2e/_suites/fleet/features/linux_integration.feature
e2e/_suites/fleet/features/fleet_mode_agent.feature

The stand_alone_agent file is the exception:
e2e/_suites/fleet/features/stand_alone_agent.feature

  • we have opportunity there to improve the stand-alone test to include Docker usage with Fleet Server. And it may be a great and easy way to spin up a 2nd Agent to connect to the first (the first Agent also running the Fleet Server, and the 2nd being a 'normal' Agent)

So, knowing this now, I'm not sure if we want to keep this first PR small and merge it so we have some passing scenario knowing the others will fail, or if we'll want to push forward with slightly larger impact across more files after we see it working.

Comment thread
mdelapenya marked this conversation as resolved.
Outdated
When a "<os>" agent is deployed to Fleet with "tar" installer in fleet-server mode
Comment thread
mdelapenya marked this conversation as resolved.
Then the agent is listed in Fleet as "online"

@centos
Examples: Centos
| os |
| centos |

@debian
Examples: Debian
| os |
| debian |
85 changes: 61 additions & 24 deletions e2e/_suites/fleet/fleet.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func (fts *FleetTestSuite) beforeScenario() {
fts.Version = agentVersion

// create policy with system monitoring enabled
defaultPolicy, err := getAgentDefaultPolicy()
defaultPolicy, err := getAgentDefaultPolicy("is_default")
if err != nil {
log.WithFields(log.Fields{
"err": err,
Expand Down Expand Up @@ -158,6 +158,9 @@ func (fts *FleetTestSuite) contributeSteps(s *godog.ScenarioContext) {
s.Step(`^the policy response will be shown in the Security App$`, fts.thePolicyResponseWillBeShownInTheSecurityApp)
s.Step(`^the policy is updated to have "([^"]*)" in "([^"]*)" mode$`, fts.thePolicyIsUpdatedToHaveMode)
s.Step(`^the policy will reflect the change in the Security App$`, fts.thePolicyWillReflectTheChangeInTheSecurityApp)

// fleet server steps
s.Step(`^a "([^"]*)" agent is deployed to Fleet with "([^"]*)" installer in fleet-server mode$`, fts.anAgentIsDeployedToFleetWithInstallerInFleetMode)
}

func (fts *FleetTestSuite) anStaleAgentIsDeployedToFleetWithInstaller(image, version, installerType string) error {
Expand Down Expand Up @@ -271,10 +274,15 @@ func (fts *FleetTestSuite) agentInVersion(version string) error {

// supported installers: tar, systemd
func (fts *FleetTestSuite) anAgentIsDeployedToFleetWithInstaller(image string, installerType string) error {
return fts.anAgentIsDeployedToFleetWithInstallerAndFleetServer(image, installerType, false)
}

func (fts *FleetTestSuite) anAgentIsDeployedToFleetWithInstallerAndFleetServer(image string, installerType string, isFleetServer bool) error {
log.WithFields(log.Fields{
"image": image,
"installer": installerType,
}).Trace("Deploying an agent to Fleet with base image")
"fleetServer": isFleetServer,
"image": image,
"installer": installerType,
}).Trace("Deploying an agent to Fleet with base image and fleet server")

fts.Image = image
fts.InstallerType = installerType
Expand All @@ -296,15 +304,16 @@ func (fts *FleetTestSuite) anAgentIsDeployedToFleetWithInstaller(image string, i
fts.CurrentToken = tokenJSONObject.Path("api_key").Data().(string)
fts.CurrentTokenID = tokenJSONObject.Path("id").Data().(string)

err = deployAgentToFleet(installer, containerName, fts.CurrentToken)
var fleetConfig *FleetConfig
fleetConfig, err = deployAgentToFleet(installer, containerName, fts.CurrentToken, isFleetServer)
fts.Cleanup = true
if err != nil {
return err
}

// the installation process for TAR includes the enrollment
if installer.installerType != "tar" {
err = installer.EnrollFn(fts.CurrentToken)
err = installer.EnrollFn(fleetConfig)
if err != nil {
return err
}
Expand Down Expand Up @@ -427,10 +436,10 @@ func (fts *FleetTestSuite) theAgentIsListedInFleetWithStatus(desiredStatus strin
"status": desiredStatus,
}).Info("The Agent is not present in Fleet, as expected")
return nil
} else if desiredStatus == "online" {
retryCount++
return fmt.Errorf("The agent is not present in Fleet, but it should")
}

retryCount++
return fmt.Errorf("The agent is not present in Fleet in the '%s' status, but it should", desiredStatus)
}

isAgentInStatus, err := isAgentInStatus(agentID, desiredStatus)
Expand Down Expand Up @@ -593,7 +602,12 @@ func (fts *FleetTestSuite) theAgentIsReenrolledOnTheHost() error {

installer := fts.getInstaller()

err := installer.EnrollFn(fts.CurrentToken)
cfg, err := NewFleetConfig(fts.CurrentToken, false)
if err != nil {
return err
}

err = installer.EnrollFn(cfg)
if err != nil {
return err
}
Expand Down Expand Up @@ -638,7 +652,7 @@ func (fts *FleetTestSuite) thePolicyShowsTheDatasourceAdded(packageName string)
fts.Integration = integration

configurationIsPresentFn := func() error {
defaultPolicy, err := getAgentDefaultPolicy()
defaultPolicy, err := getAgentDefaultPolicy("is_default")
if err != nil {
log.WithFields(log.Fields{
"error": err,
Expand Down Expand Up @@ -1009,14 +1023,14 @@ func (fts *FleetTestSuite) anAttemptToEnrollANewAgentFails() error {

containerName := fmt.Sprintf("%s_%s_%s_%d", profile, fts.Image+"-systemd", ElasticAgentServiceName, 2) // name of the new container

err := deployAgentToFleet(installer, containerName, fts.CurrentToken)
fleetConfig, err := deployAgentToFleet(installer, containerName, fts.CurrentToken, false)
// the installation process for TAR includes the enrollment
if installer.installerType != "tar" {
if err != nil {
return err
}

err = installer.EnrollFn(fts.CurrentToken)
err = installer.EnrollFn(fleetConfig)
if err == nil {
err = fmt.Errorf("The agent was enrolled although the token was previously revoked")

Expand Down Expand Up @@ -1236,7 +1250,7 @@ func createFleetToken(name string, policyID string) (*gabs.Container, error) {
return tokenItem, nil
}

func deployAgentToFleet(installer ElasticAgentInstaller, containerName string, token string) error {
func deployAgentToFleet(installer ElasticAgentInstaller, containerName string, token string, isFleetServer bool) (*FleetConfig, error) {
profile := installer.profile // name of the runtime dependencies compose file
service := installer.service // name of the service
serviceTag := installer.tag // docker tag of the service
Expand All @@ -1259,24 +1273,31 @@ func deployAgentToFleet(installer ElasticAgentInstaller, containerName string, t
"service": service,
"tag": serviceTag,
}).Error("Could not run the target box")
return err
return nil, err
}

err = installer.PreInstallFn()
if err != nil {
return err
return nil, err
}

cfg, cfgError := NewFleetConfig(token, isFleetServer)
if cfgError != nil {
return nil, cfgError
}

err = installer.InstallFn(containerName, token)
err = installer.InstallFn(cfg)
if err != nil {
return err
return nil, err
}

return installer.PostInstallFn()
return cfg, installer.PostInstallFn()
}

// getAgentDefaultPolicy sends a GET request to Fleet for the existing default policy
func getAgentDefaultPolicy() (*gabs.Container, error) {
// getAgentDefaultPolicy sends a GET request to Fleet for the existing default policy, using the
// "defaultPolicyFieldName" passed as parameter as field to be used to find the policy in list
// of fleet policies
func getAgentDefaultPolicy(defaultPolicyFieldName string) (*gabs.Container, error) {
r := createDefaultHTTPRequest(ingestManagerAgentPoliciesURL)
body, err := curl.Get(r)
if err != nil {
Expand Down Expand Up @@ -1304,10 +1325,21 @@ func getAgentDefaultPolicy() (*gabs.Container, error) {
"count": len(policies.Children()),
}).Trace("Fleet policies retrieved")

// TODO: perform a strong check to capture default policy
defaultPolicy := policies.Index(0)
for _, policy := range policies.Children() {
if !policy.Exists(defaultPolicyFieldName) {
continue
}

if policy.Path(defaultPolicyFieldName).Data().(bool) {
log.WithFields(log.Fields{
"field": defaultPolicyFieldName,
"policy": policy,
}).Trace("Default Policy was found")
return policy, nil
}
}

return defaultPolicy, nil
return nil, fmt.Errorf("Default policy was not found with '%s' field equals to 'true'", defaultPolicyFieldName)
}

func getAgentEvents(applicationName string, agentID string, packagePolicyID string, updatedAt string) error {
Expand Down Expand Up @@ -1483,6 +1515,11 @@ func isAgentInStatus(agentID string, desiredStatus string) (bool, error) {

jsonResponse, err := gabs.ParseJSON([]byte(body))

log.WithFields(log.Fields{
"agentID": agentID,
"desiredStatus": desiredStatus,
}).Info(jsonResponse)

agentStatus := jsonResponse.Path("item.status").Data().(string)

return (strings.ToLower(agentStatus) == strings.ToLower(desiredStatus)), nil
Expand Down
65 changes: 65 additions & 0 deletions e2e/_suites/fleet/fleet_server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// 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 main

import (
"fmt"

log "github.com/sirupsen/logrus"
)

// FleetConfig represents the configuration for Fleet Server when building the enrollment command
type FleetConfig struct {
EnrollmentToken string
ElasticsearchPort int
ElasticsearchURI string
ElasticsearchCredentials string
// server
ServerPolicyID string
}

// NewFleetConfig builds a new configuration for the fleet agent, defaulting ES credentials, URI and port.
// If the 'fleetServerMode' flag is true, the it will also retrieve the default policy ID for fleet server
func NewFleetConfig(token string, fleetServerMode bool) (*FleetConfig, error) {
cfg := &FleetConfig{
EnrollmentToken: token,
ElasticsearchCredentials: "elastic:changeme",
ElasticsearchPort: 9200,
ElasticsearchURI: "elasticsearch",
}

if fleetServerMode {
defaultFleetServerPolicy, err := getAgentDefaultPolicy("is_default_fleet_server")
if err != nil {
return nil, err
}

cfg.ServerPolicyID = defaultFleetServerPolicy.Path("id").Data().(string)

log.WithFields(log.Fields{
"elasticsearch": cfg.ElasticsearchURI,
"elasticsearchPort": cfg.ElasticsearchPort,
"policyID": cfg.ServerPolicyID,
"token": cfg.EnrollmentToken,
}).Debug("Fleet Server config created")
}

return cfg, nil
}

func (cfg FleetConfig) flags() []string {
baseFlags := []string{"--force", "--insecure", "--enrollment-token=" + cfg.EnrollmentToken}

if cfg.ServerPolicyID != "" {
return append(baseFlags, "--fleet-server", fmt.Sprintf("http://%s@%s:%d", cfg.ElasticsearchCredentials, cfg.ElasticsearchURI, cfg.ElasticsearchPort), "--fleet-server-policy", cfg.ServerPolicyID)
}

return append(baseFlags, "--kibana-url", "http://kibana:5601")
}

func (fts *FleetTestSuite) anAgentIsDeployedToFleetWithInstallerInFleetMode(image string, installerType string) error {
fts.ElasticAgentStopped = true
return fts.anAgentIsDeployedToFleetWithInstallerAndFleetServer(image, installerType, true)
}
17 changes: 9 additions & 8 deletions e2e/_suites/fleet/installers.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (

// InstallerPackage represents the operations that can be performed by an installer package type
type InstallerPackage interface {
Install(containerName string, token string) error
Install(cfg *FleetConfig) error
InstallCerts() error
PrintLogs(containerName string) error
Postinstall() error
Expand Down Expand Up @@ -119,7 +119,7 @@ func NewDEBPackage(binaryName string, profile string, image string, service stri
}

// Install installs a DEB package
func (i *DEBPackage) Install(containerName string, token string) error {
func (i *DEBPackage) Install(cfg *FleetConfig) error {
return i.extractPackage([]string{"apt", "install", "/" + i.binaryName, "-y"})
}

Expand Down Expand Up @@ -182,7 +182,7 @@ func NewDockerPackage(binaryName string, profile string, image string, service s
}

// Install installs a Docker package
func (i *DockerPackage) Install(containerName string, token string) error {
func (i *DockerPackage) Install(cfg *FleetConfig) error {
log.Trace("No install commands for Docker packages")
return nil
}
Expand Down Expand Up @@ -267,7 +267,7 @@ func NewRPMPackage(binaryName string, profile string, image string, service stri
}

// Install installs a RPM package
func (i *RPMPackage) Install(containerName string, token string) error {
func (i *RPMPackage) Install(cfg *FleetConfig) error {
return i.extractPackage([]string{"yum", "localinstall", "/" + i.binaryName, "-y"})
}

Expand Down Expand Up @@ -329,10 +329,11 @@ func NewTARPackage(binaryName string, profile string, image string, service stri
}

// Install installs a TAR package
func (i *TARPackage) Install(containerName string, token string) error {
func (i *TARPackage) Install(cfg *FleetConfig) error {
// install the elastic-agent to /usr/bin/elastic-agent using command
binary := fmt.Sprintf("/elastic-agent/%s", i.artifact)
args := []string{"--force", "--insecure", "--enrollment-token=" + token, "--kibana-url", "http://kibana:5601"}

args := cfg.flags()

err := runElasticAgentCommand(i.profile, i.image, i.service, binary, "install", args)
if err != nil {
Expand Down Expand Up @@ -374,8 +375,8 @@ func (i *TARPackage) Preinstall() error {

// simplify layout
cmds := [][]string{
[]string{"rm", "-fr", "/elastic-agent"},
[]string{"mv", fmt.Sprintf("/%s-%s-%s-%s", i.artifact, i.version, i.OS, i.arch), "/elastic-agent"},
{"rm", "-fr", "/elastic-agent"},

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove some leftovers: the type is automatically inferred by the Go compiler

{"mv", fmt.Sprintf("/%s-%s-%s-%s", i.artifact, i.version, i.OS, i.arch), "/elastic-agent"},
}
for _, cmd := range cmds {
err = execCommandInService(i.profile, i.image, i.service, cmd, false)
Expand Down
Loading