From 27b9e8773db4185e92565c4265ed78bffa7e432e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 10 Sep 2020 11:40:26 +0200 Subject: [PATCH 1/3] feat: support creating (and removing) a policy for each scenario (#279) * feat: support creating a policy per scenario, not reusing default one * chore: extract tear down code to afterScenario methods * fix: move creation of the policy to the before scenario hook * fix: use proper URL after bad copy&paste * chore: add debug logs for removals # Conflicts: # e2e/_suites/ingest-manager/fleet.go # e2e/_suites/ingest-manager/ingest-manager_test.go --- e2e/_suites/ingest-manager/fleet.go | 181 +++++++++++++++--- .../ingest-manager/ingest-manager_test.go | 51 +---- e2e/_suites/ingest-manager/stand-alone.go | 20 ++ 3 files changed, 179 insertions(+), 73 deletions(-) diff --git a/e2e/_suites/ingest-manager/fleet.go b/e2e/_suites/ingest-manager/fleet.go index 477aaffb73..e031bad10d 100644 --- a/e2e/_suites/ingest-manager/fleet.go +++ b/e2e/_suites/ingest-manager/fleet.go @@ -44,6 +44,74 @@ type FleetTestSuite struct { ConfigUpdatedAt string // the moment the configuration was updated } +// afterScenario destroys the state created by a scenario +func (fts *FleetTestSuite) afterScenario() { + serviceManager := services.NewServiceManager() + + err := fts.unenrollHostname(true) + if err != nil { + log.WithFields(log.Fields{ + "err": err, + "hostname": fts.Hostname, + }).Warn("The agentIDs for the hostname could not be unenrolled") + } + + serviceName := fts.Image + if !developerMode { + _ = serviceManager.RemoveServicesFromCompose(IngestManagerProfileName, []string{serviceName}, profileEnv) + } else { + log.WithField("service", serviceName).Info("Because we are running in development mode, the service won't be stopped") + } + + err = fts.removeToken() + if err != nil { + log.WithFields(log.Fields{ + "err": err, + "tokenID": fts.CurrentTokenID, + }).Warn("The enrollment token could not be deleted") + } + + err = deleteIntegrationFromConfiguration(fts.Integration, fts.ConfigID) + if err != nil { + log.WithFields(log.Fields{ + "err": err, + "packageConfigID": fts.Integration.packageConfigID, + "configurationID": fts.ConfigID, + }).Warn("The integration could not be deleted from the configuration") + } + + err = fts.removeConfiguration() + if err != nil { + log.WithFields(log.Fields{ + "err": err, + "configurationID": fts.ConfigID, + }).Warn("The configuration could not be deleted") + } + + // clean up fields + fts.CurrentTokenID = "" + fts.Image = "" + fts.Hostname = "" + fts.ConfigID = "" +} + +// beforeScenario creates the state needed by a scenario +func (fts *FleetTestSuite) beforeScenario() { + fts.Cleanup = false + + // create configuration with system monitoring enabled + newConfiguration, err := createFleetConfig(true) + if err != nil { + log.WithFields(log.Fields{ + "err": err, + }).Warn("The configuration could not be created") + + return + } + + fts.ConfigID = newConfiguration.Path("id").Data().(string) +} + func (fts *FleetTestSuite) contributeSteps(s *godog.Suite) { s.Step(`^a "([^"]*)" agent is deployed to Fleet$`, fts.anAgentIsDeployedToFleet) s.Step(`^the agent is listed in Fleet as "([^"]*)"$`, fts.theAgentIsListedInFleetWithStatus) @@ -168,12 +236,6 @@ func (fts *FleetTestSuite) setup() error { return err } - defaultConfig, err := getAgentDefaultConfig() - if err != nil { - return err - } - fts.ConfigID = defaultConfig.Path("id").Data().(string) - return nil } @@ -793,6 +855,29 @@ func (fts *FleetTestSuite) anAttemptToEnrollANewAgentFails() error { return nil } +func (fts *FleetTestSuite) removeConfiguration() error { + removeConfigurationURL := ingestManagerAgentConfigsURL + "/delete" + postReq := createDefaultHTTPRequest(removeConfigurationURL) + postReq.Payload = `{"agentConfigId":"` + fts.ConfigID + `"}` + + body, err := curl.Post(postReq) + if err != nil { + log.WithFields(log.Fields{ + "configurationID": fts.ConfigID, + "body": body, + "error": err, + "url": removeConfigurationURL, + }).Error("Could not delete configuration") + return err + } + + log.WithFields(log.Fields{ + "configurationID": fts.ConfigID, + }).Debug("The configuration was deleted") + + return nil +} + func (fts *FleetTestSuite) removeToken() error { revokeTokenURL := fleetEnrollmentTokenURL + "/" + fts.CurrentTokenID deleteReq := createDefaultHTTPRequest(revokeTokenURL) @@ -808,6 +893,10 @@ func (fts *FleetTestSuite) removeToken() error { return err } + log.WithFields(log.Fields{ + "tokenID": fts.CurrentTokenID, + }).Debug("The token was deleted") + return nil } @@ -916,6 +1005,46 @@ func createDefaultHTTPRequest(url string) curl.HTTPRequest { } } +// createFleetConfig() sends a POST request to Fleet creating a new test configuration +func createFleetConfig(sysMonitoring bool) (*gabs.Container, error) { + name := e2e.RandomString(8) + url := fmt.Sprintf(ingestManagerAgentConfigsURL+"?sys_monitoring=%t", sysMonitoring) + postReq := createDefaultHTTPRequest(url) + postReq.Payload = `{ + "description": "Test configuration ` + name + `", + "namespace": "default", + "monitoring_enabled": ["logs", "metrics"], + "name": "test-configuration-` + name + `" + }` + + body, err := curl.Post(postReq) + if err != nil { + log.WithFields(log.Fields{ + "body": body, + "error": err, + "url": url, + }).Error("Could not create Fleet configuration") + return nil, err + } + + jsonParsed, err := gabs.ParseJSON([]byte(body)) + if err != nil { + log.WithFields(log.Fields{ + "error": err, + "responseBody": body, + }).Error("Could not parse response into JSON") + return nil, err + } + + configurationItem := jsonParsed.Path("item") + + log.WithFields(log.Fields{ + "id": configurationItem.Path("id").Data().(string), + }).Debug("Fleet configuration created") + + return configurationItem, nil +} + // createFleetToken sends a POST request to Fleet creating a new token with a name func createFleetToken(name string, configID string) (*gabs.Container, error) { postReq := createDefaultHTTPRequest(fleetEnrollmentTokenURL) @@ -1055,7 +1184,7 @@ func getAgentDefaultConfig() (*gabs.Container, error) { return defaultConfig, nil } -func getAgentEvents(applicationName string, agentID string, packagePolicyID string, updatedAt string) error { +func getAgentEvents(applicationName string, agentID string, packageConfigurationID string, updatedAt string) error { url := fmt.Sprintf(fleetAgentEventsURL, agentID) getReq := createDefaultHTTPRequest(url) getReq.QueryString = "page=1&perPage=20" @@ -1063,12 +1192,12 @@ func getAgentEvents(applicationName string, agentID string, packagePolicyID stri body, err := curl.Get(getReq) if err != nil { log.WithFields(log.Fields{ - "agentID": agentID, - "application": applicationName, - "body": body, - "error": err, - "packagePolicyID": packagePolicyID, - "url": url, + "agentID": agentID, + "application": applicationName, + "body": body, + "error": err, + "packageConfigurationID": packageConfigurationID, + "url": url, }).Error("Could not get agent events from Fleet") return err } @@ -1088,31 +1217,31 @@ func getAgentEvents(applicationName string, agentID string, packagePolicyID stri timestamp := item.Path("timestamp").Data().(string) log.WithFields(log.Fields{ - "agentID": agentID, - "application": applicationName, - "event_at": timestamp, - "message": message, - "packagePolicyID": packagePolicyID, - "updated_at": updatedAt, + "agentID": agentID, + "application": applicationName, + "event_at": timestamp, + "message": message, + "packageConfigurationID": packageConfigurationID, + "updated_at": updatedAt, }).Trace("Event found") matches := (strings.Contains(message, applicationName) && strings.Contains(message, "["+agentID+"]: State changed to") && - strings.Contains(message, "Protecting with policy {"+packagePolicyID+"}")) + strings.Contains(message, "Protecting with config {"+packageConfigurationID+"}")) if matches && timestamp > updatedAt { log.WithFields(log.Fields{ - "application": applicationName, - "event_at": timestamp, - "packagePolicyID": packagePolicyID, - "updated_at": updatedAt, - "message": message, + "application": applicationName, + "event_at": timestamp, + "packageConfigurationID": packageConfigurationID, + "updated_at": updatedAt, + "message": message, }).Info("Event after the update was found") return nil } } - return fmt.Errorf("No %s events where found for the agent in the %s policy", applicationName, packagePolicyID) + return fmt.Errorf("No %s events where found for the agent in the %s configuration", applicationName, packageConfigurationID) } // getAgentID sends a GET request to Fleet for a existing hostname diff --git a/e2e/_suites/ingest-manager/ingest-manager_test.go b/e2e/_suites/ingest-manager/ingest-manager_test.go index bf76b84e65..9ccc6ce840 100644 --- a/e2e/_suites/ingest-manager/ingest-manager_test.go +++ b/e2e/_suites/ingest-manager/ingest-manager_test.go @@ -123,6 +123,8 @@ func IngestManagerFeatureContext(s *godog.Suite) { log.Trace("Before Ingest Manager scenario") imts.StandAlone.Cleanup = false + + imts.Fleet.beforeScenario() }) s.AfterSuite(func() { if !developerMode { @@ -162,57 +164,12 @@ func IngestManagerFeatureContext(s *godog.Suite) { log.Trace("After Ingest Manager scenario") if imts.StandAlone.Cleanup { - serviceName := ElasticAgentServiceName - if !developerMode { - _ = serviceManager.RemoveServicesFromCompose(IngestManagerProfileName, []string{serviceName}, profileEnv) - } else { - log.WithField("service", serviceName).Info("Because we are running in development mode, the service won't be stopped") - } - - if _, err := os.Stat(imts.StandAlone.AgentConfigFilePath); err == nil { - os.Remove(imts.StandAlone.AgentConfigFilePath) - log.WithFields(log.Fields{ - "path": imts.StandAlone.AgentConfigFilePath, - }).Debug("Elastic Agent configuration file removed.") - } + imts.StandAlone.afterScenario() } if imts.Fleet.Cleanup { - err := imts.Fleet.unenrollHostname(true) - if err != nil { - log.WithFields(log.Fields{ - "err": err, - "hostname": imts.Fleet.Hostname, - }).Warn("The agentIDs for the hostname could not be unenrolled") - } - - serviceName := imts.Fleet.Image - if !developerMode { - _ = serviceManager.RemoveServicesFromCompose(IngestManagerProfileName, []string{serviceName}, profileEnv) - } else { - log.WithField("service", serviceName).Info("Because we are running in development mode, the service won't be stopped") - } - - err = imts.Fleet.removeToken() - if err != nil { - log.WithFields(log.Fields{ - "err": err, - "tokenID": imts.Fleet.CurrentTokenID, - }).Warn("The enrollment token could not be deleted") - } - - err = deleteIntegrationFromConfiguration(imts.Fleet.Integration, imts.Fleet.ConfigID) - if err != nil { - log.WithFields(log.Fields{ - "err": err, - "packageConfigID": imts.Fleet.Integration.packageConfigID, - "configurationID": imts.Fleet.ConfigID, - }).Warn("The integration could not be deleted from the configuration") - } + imts.Fleet.afterScenario() } - - imts.Fleet.Image = "" - imts.StandAlone.Hostname = "" }) } diff --git a/e2e/_suites/ingest-manager/stand-alone.go b/e2e/_suites/ingest-manager/stand-alone.go index d0af189454..2f76724a16 100644 --- a/e2e/_suites/ingest-manager/stand-alone.go +++ b/e2e/_suites/ingest-manager/stand-alone.go @@ -6,6 +6,7 @@ package main import ( "fmt" + "os" "strings" "time" @@ -37,6 +38,25 @@ type StandAloneTestSuite struct { RuntimeDependenciesStartDate time.Time } +// afterScenario destroys the state created by a scenario +func (sats *StandAloneTestSuite) afterScenario() { + serviceManager := services.NewServiceManager() + serviceName := ElasticAgentServiceName + + if !developerMode { + _ = serviceManager.RemoveServicesFromCompose(IngestManagerProfileName, []string{serviceName}, profileEnv) + } else { + log.WithField("service", serviceName).Info("Because we are running in development mode, the service won't be stopped") + } + + if _, err := os.Stat(sats.AgentConfigFilePath); err == nil { + os.Remove(sats.AgentConfigFilePath) + log.WithFields(log.Fields{ + "path": sats.AgentConfigFilePath, + }).Debug("Elastic Agent configuration file removed.") + } +} + func (sats *StandAloneTestSuite) contributeSteps(s *godog.Suite) { s.Step(`^a stand-alone agent is deployed$`, sats.aStandaloneAgentIsDeployed) s.Step(`^there is new data in the index from agent$`, sats.thereIsNewDataInTheIndexFromAgent) From 380086a930f3e039d614493b63c322328324241f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 10 Sep 2020 20:16:21 +0200 Subject: [PATCH 2/3] fix: remove references to the default policy (#281) Internally, we were using the ID, so it should work as expected, but the specs were not in sync # Conflicts: # e2e/_suites/ingest-manager/features/agent_endpoint_integration.feature # e2e/_suites/ingest-manager/fleet.go --- .../agent_endpoint_integration.feature | 6 +++--- e2e/_suites/ingest-manager/fleet.go | 20 +++++++++---------- e2e/go.mod | 1 + 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/e2e/_suites/ingest-manager/features/agent_endpoint_integration.feature b/e2e/_suites/ingest-manager/features/agent_endpoint_integration.feature index 7aa93e2480..2d9c07e449 100644 --- a/e2e/_suites/ingest-manager/features/agent_endpoint_integration.feature +++ b/e2e/_suites/ingest-manager/features/agent_endpoint_integration.feature @@ -6,8 +6,8 @@ Feature: Agent Endpoint Integration Scenario: Adding the Endpoint Integration to an Agent makes the host to show in Security App Given a "centos" agent is deployed to Fleet And the agent is listed in Fleet as "online" - When the "Elastic Endpoint Security" integration is "added" in the "default" configuration - Then the "Elastic Endpoint Security" datasource is shown in the "default" configuration as added + When the "Elastic Endpoint Security" integration is "added" in the configuration + Then the "Elastic Endpoint Security" datasource is shown in the configuration as added And the host name is shown in the Administration view in the Security App as "online" @endpoint-policy-check @@ -32,7 +32,7 @@ Scenario: Un-enrolling Elastic Agent stops Elastic Endpoint @deploy-endpoint-then-remove-it-from-configuration Scenario: Removing Endpoint from Agent configuration stops the connected Endpoint Given an Endpoint is successfully deployed with a "centos" Agent - When the "Elastic Endpoint Security" integration is "removed" in the "default" configuration + When the "Elastic Endpoint Security" integration is "removed" in the configuration Then the agent is listed in Fleet as "online" But the host name is not shown in the Administration view in the Security App And the "elastic-endpoint" process is in the "stopped" state on the host diff --git a/e2e/_suites/ingest-manager/fleet.go b/e2e/_suites/ingest-manager/fleet.go index e031bad10d..aa2333d342 100644 --- a/e2e/_suites/ingest-manager/fleet.go +++ b/e2e/_suites/ingest-manager/fleet.go @@ -124,8 +124,8 @@ func (fts *FleetTestSuite) contributeSteps(s *godog.Suite) { s.Step(`^the "([^"]*)" process is "([^"]*)" on the host$`, fts.processStateChangedOnTheHost) // endpoint steps - s.Step(`^the "([^"]*)" integration is "([^"]*)" in the "([^"]*)" configuration$`, fts.theIntegrationIsOperatedInTheConfiguration) - s.Step(`^the "([^"]*)" datasource is shown in the "([^"]*)" configuration as added$`, fts.theConfigurationShowsTheDatasourceAdded) + s.Step(`^the "([^"]*)" integration is "([^"]*)" in the configuration$`, fts.theIntegrationIsOperatedInTheConfiguration) + s.Step(`^the "([^"]*)" datasource is shown in the configuration as added$`, fts.theConfigurationShowsTheDatasourceAdded) s.Step(`^the host name is shown in the Administration view in the Security App as "([^"]*)"$`, fts.theHostNameIsShownInTheAdminViewInTheSecurityApp) s.Step(`^the host name is not shown in the Administration view in the Security App$`, fts.theHostNameIsNotShownInTheAdminViewInTheSecurityApp) s.Step(`^an Endpoint is successfully deployed with a "([^"]*)" Agent$`, fts.anEndpointIsSuccessfullyDeployedWithAgent) @@ -435,10 +435,10 @@ func (fts *FleetTestSuite) theEnrollmentTokenIsRevoked() error { return nil } -func (fts *FleetTestSuite) theConfigurationShowsTheDatasourceAdded(packageName string, configurationName string) error { +func (fts *FleetTestSuite) theConfigurationShowsTheDatasourceAdded(packageName string) error { log.WithFields(log.Fields{ - "configuration": configurationName, - "package": packageName, + "configurationID": fts.ConfigID, + "package": packageName, }).Trace("Checking if the configuration shows the package added") maxTimeout := time.Minute @@ -499,11 +499,11 @@ func (fts *FleetTestSuite) theConfigurationShowsTheDatasourceAdded(packageName s return nil } -func (fts *FleetTestSuite) theIntegrationIsOperatedInTheConfiguration(packageName string, action string, configurationName string) error { +func (fts *FleetTestSuite) theIntegrationIsOperatedInTheConfiguration(packageName string, action string) error { log.WithFields(log.Fields{ - "action": action, - "configuration": configurationName, - "package": packageName, + "action": action, + "configurationID": fts.ConfigID, + "package": packageName, }).Trace("Doing an operation for a package on a configuration") if strings.ToLower(action) == actionADDED { @@ -655,7 +655,7 @@ func (fts *FleetTestSuite) anEndpointIsSuccessfullyDeployedWithAgent(image strin } // we use integration's title - return fts.theIntegrationIsOperatedInTheConfiguration("Elastic Endpoint Security", actionADDED, "default") + return fts.theIntegrationIsOperatedInTheConfiguration("Elastic Endpoint Security", actionADDED) } func (fts *FleetTestSuite) thePolicyResponseWillBeShownInTheSecurityApp() error { diff --git a/e2e/go.mod b/e2e/go.mod index 6ad7751c0d..b23989d3ad 100644 --- a/e2e/go.mod +++ b/e2e/go.mod @@ -6,6 +6,7 @@ require ( github.com/Jeffail/gabs/v2 v2.5.1 github.com/cenkalti/backoff/v4 v4.0.2 github.com/cucumber/godog v0.10.0 + github.com/cucumber/messages-go/v10 v10.0.3 github.com/elastic/e2e-testing/cli v0.0.0-20200717181709-15d2db53ded7 github.com/elastic/go-elasticsearch/v8 v8.0.0-20190731061900-ea052088db25 github.com/sirupsen/logrus v1.4.2 From 1f65cc9e0f3865acb27f53d279abd8c38e505843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 10 Sep 2020 22:13:18 +0200 Subject: [PATCH 3/3] chore: remove dependency added by mistake --- e2e/go.mod | 1 - 1 file changed, 1 deletion(-) diff --git a/e2e/go.mod b/e2e/go.mod index b23989d3ad..6ad7751c0d 100644 --- a/e2e/go.mod +++ b/e2e/go.mod @@ -6,7 +6,6 @@ require ( github.com/Jeffail/gabs/v2 v2.5.1 github.com/cenkalti/backoff/v4 v4.0.2 github.com/cucumber/godog v0.10.0 - github.com/cucumber/messages-go/v10 v10.0.3 github.com/elastic/e2e-testing/cli v0.0.0-20200717181709-15d2db53ded7 github.com/elastic/go-elasticsearch/v8 v8.0.0-20190731061900-ea052088db25 github.com/sirupsen/logrus v1.4.2