-
Notifications
You must be signed in to change notification settings - Fork 39
feat: add first scenario for Fleet Server #900
Changes from 11 commits
b1931c0
6f54550
ad0d17f
e52c8b6
e8bee02
bb62c4f
d8e57ce
6189bff
d9da8b0
830eb12
eaa68f2
bc9e1ff
4ae1019
721aa21
f5a9f46
eb8f013
e9edde9
356750f
f2244ad
7358928
bcd69ee
173004f
cb57360
6865e98
6d60a6d
04c7fc8
e5ed65c
09e4325
491eb17
a557f88
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
| When a "<os>" agent is deployed to Fleet with "tar" installer in fleet-server mode | ||
| Then Fleet server is enabled | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @EricDavisX @blakerouse I wrote this Could you help me here in writing the right expected behaviour? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not too familiar with the structure of these files so I might not be fully understanding the context. I believe your asking if its possible to known if the Fleet Server is running correct. The simplest way is to check that the Agent is reported Healthy in Kibana. That might seem to simple but the only way for the Agent running a Fleet Server to show in Kibana as healthy is if it can communicate to its local Fleet Server and that Fleet Server can write to elasticsearch.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Never mind about the code structure yet, I'm still interested in the behavior of the product without considering internal details/implementations. With that in mind:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The check can be the same as for all Agents, that it is listed as 'healthy' in the Agents API list call. A secondary check could be to assess if the Fleet Server process is running on the host, as noted in this example: This example is from macOS, but the the process name is the same. |
||
|
|
||
| @centos | ||
| Examples: Centos | ||
| | os | | ||
| | centos | | ||
|
|
||
| @debian | ||
| Examples: Debian | ||
| | os | | ||
| | debian | | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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, | ||
|
|
@@ -158,6 +158,10 @@ 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) | ||
| s.Step(`^Fleet server is enabled$`, fts.fleetServerIsEnabled) | ||
| } | ||
|
|
||
| func (fts *FleetTestSuite) anStaleAgentIsDeployedToFleetWithInstaller(image, version, installerType string) error { | ||
|
|
@@ -271,10 +275,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 | ||
|
|
@@ -296,15 +305,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 | ||
| } | ||
|
|
@@ -593,7 +603,7 @@ func (fts *FleetTestSuite) theAgentIsReenrolledOnTheHost() error { | |
|
|
||
| installer := fts.getInstaller() | ||
|
|
||
| err := installer.EnrollFn(fts.CurrentToken) | ||
| err := installer.EnrollFn(NewFleetConfig(fts.CurrentToken)) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
@@ -638,7 +648,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, | ||
|
|
@@ -1009,14 +1019,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") | ||
|
|
||
|
|
@@ -1236,7 +1246,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 | ||
|
|
@@ -1259,24 +1269,38 @@ 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 | ||
| } | ||
|
|
||
| err = installer.InstallFn(containerName, token) | ||
| var fleetConfig *FleetConfig | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Didn't actually tried, but I think it would be a bit simpler to have just 1 kind of config and 1 way of creating it, eg: Credentials, url and port can also be hardcoded in the only place they are used.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, let me send a follow-up commit with that, thanks!
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Implemented in f5a9f46 |
||
| if isFleetServer { | ||
| cfg, cfgError := NewFleetServerConfig(token) | ||
| if cfgError != nil { | ||
| return nil, cfgError | ||
| } | ||
| fleetConfig = cfg | ||
| } else { | ||
| fleetConfig = NewFleetConfig(token) | ||
| } | ||
|
|
||
| err = installer.InstallFn(fleetConfig) | ||
|
|
||
| if err != nil { | ||
| return err | ||
| return nil, err | ||
| } | ||
|
|
||
| return installer.PostInstallFn() | ||
| return fleetConfig, 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 { | ||
|
|
@@ -1304,10 +1328,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 { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| // 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" | ||
|
|
||
| "github.com/cucumber/godog" | ||
| 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 | ||
| func NewFleetConfig(token string) *FleetConfig { | ||
| return &FleetConfig{ | ||
| EnrollmentToken: token, | ||
| ElasticsearchCredentials: "elastic:changeme", | ||
| ElasticsearchPort: 9200, | ||
| ElasticsearchURI: "elasticsearch", | ||
| } | ||
| } | ||
|
|
||
| // NewFleetServerConfig builds a new configuration for the fleet server agent, defaulting credentials and port, | ||
| // also retrieving the default policy ID for fleet server | ||
| func NewFleetServerConfig(token string) (*FleetConfig, error) { | ||
| cfg := NewFleetConfig(token) | ||
|
|
||
| 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) | ||
| } | ||
|
|
||
| func (fts *FleetTestSuite) fleetServerIsEnabled() error { | ||
| log.Debug("Fleet server is enabled") | ||
| return godog.ErrPending | ||
|
mdelapenya marked this conversation as resolved.
Outdated
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
|
@@ -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"}) | ||
| } | ||
|
|
||
|
|
@@ -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 | ||
| } | ||
|
|
@@ -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"}) | ||
| } | ||
|
|
||
|
|
@@ -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 { | ||
|
|
@@ -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"}, | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.