|
7 | 7 | package integration
|
8 | 8 |
|
9 | 9 | import (
|
| 10 | + "archive/zip" |
10 | 11 | "bytes"
|
11 | 12 | "context"
|
12 | 13 | _ "embed"
|
13 | 14 | "encoding/json"
|
14 | 15 | "fmt"
|
| 16 | + "io/fs" |
15 | 17 | "os"
|
16 | 18 | "path/filepath"
|
17 | 19 | "runtime"
|
| 20 | + "slices" |
18 | 21 | "strings"
|
19 | 22 | "testing"
|
20 | 23 | "text/template"
|
@@ -646,6 +649,144 @@ func TestEndpointSecurityUnprivileged(t *testing.T) {
|
646 | 649 | }, 2*time.Minute, 10*time.Second, "Agent never became DEGRADED with root/Administrator install message")
|
647 | 650 | }
|
648 | 651 |
|
| 652 | +// TestEndpointLogsAreCollectedInDiagnostics tests that diagnostics archive contain endpoint logs |
| 653 | +func TestEndpointLogsAreCollectedInDiagnostics(t *testing.T) { |
| 654 | + info := define.Require(t, define.Requirements{ |
| 655 | + Group: Fleet, |
| 656 | + Stack: &define.Stack{}, |
| 657 | + Local: false, // requires Agent installation |
| 658 | + Sudo: true, // requires Agent installation |
| 659 | + OS: []define.OS{ |
| 660 | + {Type: define.Linux}, |
| 661 | + }, |
| 662 | + }) |
| 663 | + |
| 664 | + ctx, cn := testcontext.WithDeadline(t, context.Background(), time.Now().Add(10*time.Minute)) |
| 665 | + defer cn() |
| 666 | + |
| 667 | + // Get path to agent executable. |
| 668 | + fixture, err := define.NewFixture(t, define.Version()) |
| 669 | + require.NoError(t, err) |
| 670 | + |
| 671 | + t.Log("Enrolling the agent in Fleet") |
| 672 | + policyUUID := uuid.New().String() |
| 673 | + createPolicyReq := kibana.AgentPolicy{ |
| 674 | + Name: "test-policy-" + policyUUID, |
| 675 | + Namespace: "default", |
| 676 | + Description: "Test policy " + policyUUID, |
| 677 | + MonitoringEnabled: []kibana.MonitoringEnabledOption{ |
| 678 | + kibana.MonitoringEnabledLogs, |
| 679 | + kibana.MonitoringEnabledMetrics, |
| 680 | + }, |
| 681 | + } |
| 682 | + installOpts := atesting.InstallOpts{ |
| 683 | + NonInteractive: true, |
| 684 | + Force: true, |
| 685 | + Unprivileged: atesting.NewBool(false), |
| 686 | + } |
| 687 | + |
| 688 | + policyResp, err := tools.InstallAgentWithPolicy(ctx, t, installOpts, fixture, info.KibanaClient, createPolicyReq) |
| 689 | + require.NoErrorf(t, err, "Policy Response was: %v", policyResp) |
| 690 | + |
| 691 | + t.Cleanup(func() { |
| 692 | + t.Log("Un-enrolling Elastic Agent...") |
| 693 | + // Use a separate context as the one in the test body will have been cancelled at this point. |
| 694 | + cleanupCtx, cleanupCancel := context.WithTimeout(context.Background(), time.Minute) |
| 695 | + defer cleanupCancel() |
| 696 | + assert.NoError(t, fleettools.UnEnrollAgent(cleanupCtx, info.KibanaClient, policyResp.ID)) |
| 697 | + }) |
| 698 | + |
| 699 | + t.Log("Installing Elastic Defend") |
| 700 | + pkgPolicyResp, err := installElasticDefendPackage(t, info, policyResp.ID) |
| 701 | + require.NoErrorf(t, err, "Policy Response was: %v", pkgPolicyResp) |
| 702 | + |
| 703 | + // wait for endpoint to be healthy |
| 704 | + t.Log("Polling for endpoint-security to become Healthy") |
| 705 | + pollingCtx, pollingCancel := context.WithTimeout(ctx, endpointHealthPollingTimeout) |
| 706 | + defer pollingCancel() |
| 707 | + |
| 708 | + require.Eventually(t, |
| 709 | + func() bool { |
| 710 | + agentClient := fixture.Client() |
| 711 | + err = agentClient.Connect(ctx) |
| 712 | + if err != nil { |
| 713 | + t.Logf("error connecting to agent: %v", err) |
| 714 | + return false |
| 715 | + } |
| 716 | + defer agentClient.Disconnect() |
| 717 | + return agentAndEndpointAreHealthy(t, pollingCtx, agentClient) |
| 718 | + }, |
| 719 | + endpointHealthPollingTimeout, |
| 720 | + time.Second, |
| 721 | + "Endpoint component or units are not healthy.", |
| 722 | + ) |
| 723 | + |
| 724 | + outDir := t.TempDir() |
| 725 | + diagFile := t.Name() + ".zip" |
| 726 | + diagAbsPath := filepath.Join(outDir, diagFile) |
| 727 | + _, err = fixture.Exec(ctx, []string{"diagnostics", "-f", diagAbsPath}) |
| 728 | + require.NoError(t, err, "diagnostics command failed") |
| 729 | + require.FileExists(t, diagAbsPath, "diagnostic archive should have been created") |
| 730 | + checkDiagnosticsForEndpointFiles(t, diagAbsPath) |
| 731 | +} |
| 732 | + |
| 733 | +func checkDiagnosticsForEndpointFiles(t *testing.T, diagsPath string) { |
| 734 | + zipReader, err := zip.OpenReader(diagsPath) |
| 735 | + require.NoError(t, err, "error opening diagnostics archive") |
| 736 | + |
| 737 | + defer func(zipReader *zip.ReadCloser) { |
| 738 | + err := zipReader.Close() |
| 739 | + assert.NoError(t, err, "error closing diagnostic archive") |
| 740 | + }(zipReader) |
| 741 | + |
| 742 | + t.Logf("---- Contents of diagnostics archive") |
| 743 | + for _, file := range zipReader.File { |
| 744 | + t.Logf("%q - %+v", file.Name, file.FileHeader.FileInfo()) |
| 745 | + } |
| 746 | + t.Logf("---- End contents of diagnostics archive") |
| 747 | + // check there are files under the components/ directory |
| 748 | + endpointComponentDirName := "components/endpoint-default" |
| 749 | + endpointComponentDir, err := zipReader.Open(endpointComponentDirName) |
| 750 | + if assert.NoErrorf(t, err, "error looking up directory %q in diagnostic archive: %v", endpointComponentDirName, err) { |
| 751 | + defer func(endpointComponentDir fs.File) { |
| 752 | + err := endpointComponentDir.Close() |
| 753 | + if err != nil { |
| 754 | + assert.NoError(t, err, "error closing endpoint component directory") |
| 755 | + } |
| 756 | + }(endpointComponentDir) |
| 757 | + if assert.Implementsf(t, (*fs.ReadDirFile)(nil), endpointComponentDir, "endpoint should have a directory in the diagnostic archive under %s", endpointComponentDirName) { |
| 758 | + dirFile := endpointComponentDir.(fs.ReadDirFile) |
| 759 | + endpointFiles, err := dirFile.ReadDir(-1) |
| 760 | + assert.NoError(t, err, "error reading endpoint component directory %q in diagnostic archive", endpointComponentDirName) |
| 761 | + assert.NotEmpty(t, endpointFiles, "endpoint component directory should not be empty") |
| 762 | + } |
| 763 | + } |
| 764 | + |
| 765 | + // check endpoint logs |
| 766 | + servicesLogDirName := "logs/services" |
| 767 | + servicesLogDir, err := zipReader.Open(servicesLogDirName) |
| 768 | + if assert.NoErrorf(t, err, "error looking up directory %q in diagnostic archive: %v", servicesLogDirName, err) { |
| 769 | + defer func(servicesLogDir fs.File) { |
| 770 | + err := servicesLogDir.Close() |
| 771 | + if err != nil { |
| 772 | + assert.NoError(t, err, "error closing services logs directory") |
| 773 | + } |
| 774 | + }(servicesLogDir) |
| 775 | + if assert.Implementsf(t, (*fs.ReadDirFile)(nil), servicesLogDir, "service logs should be in a directory in the diagnostic archive under %s", servicesLogDir) { |
| 776 | + dirFile := servicesLogDir.(fs.ReadDirFile) |
| 777 | + servicesLogFiles, err := dirFile.ReadDir(-1) |
| 778 | + assert.NoError(t, err, "error reading services logs directory %q in diagnostic archive", servicesLogDirName) |
| 779 | + assert.True(t, |
| 780 | + slices.ContainsFunc(servicesLogFiles, |
| 781 | + func(entry fs.DirEntry) bool { |
| 782 | + return strings.HasPrefix(entry.Name(), "endpoint-") && strings.HasSuffix(entry.Name(), ".log") |
| 783 | + }), |
| 784 | + "service logs should contain endpoint-*.log files", |
| 785 | + ) |
| 786 | + } |
| 787 | + } |
| 788 | +} |
| 789 | + |
649 | 790 | func agentAndEndpointAreHealthy(t *testing.T, ctx context.Context, agentClient client.Client) bool {
|
650 | 791 | t.Helper()
|
651 | 792 |
|
|
0 commit comments