Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
AndersonQ committed Sep 1, 2023
1 parent 4cc28f8 commit 2b85191
Show file tree
Hide file tree
Showing 16 changed files with 449 additions and 399 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ func (e *Downloader) downloadFile(ctx context.Context, artifactName, filename, f
return "", err
}

req, err := http.NewRequest("GET", sourceURI, nil)
req, err := http.NewRequest(http.MethodGet, sourceURI, nil)
if err != nil {
return "", errors.New(err, "fetching package failed", errors.TypeNetwork, errors.M(errors.MetaKeyURI, sourceURI))
}
Expand Down Expand Up @@ -232,7 +232,11 @@ type downloadProgressReporter struct {
started time.Time
}

func newDownloadProgressReporter(log progressLogger, sourceURI string, timeout time.Duration, length int) *downloadProgressReporter {
func newDownloadProgressReporter(
log progressLogger,
sourceURI string,
timeout time.Duration,
length int) *downloadProgressReporter {
return &downloadProgressReporter{
log: log,
sourceURI: sourceURI,
Expand Down Expand Up @@ -328,7 +332,12 @@ func (dp *downloadProgressReporter) ReportFailed(err error) {
percentComplete := downloaded / dp.length * 100.0
msg = "download from %s failed at %s/%s (%.2f%% complete) @ %sps: %s"
args = []interface{}{
dp.sourceURI, units.HumanSize(downloaded), units.HumanSize(dp.length), percentComplete, units.HumanSize(bytesPerSecond), err,
dp.sourceURI,
units.HumanSize(downloaded),
units.HumanSize(dp.length),
percentComplete,
units.HumanSize(bytesPerSecond),
err,
}
} else {
// length unknown so provide the amount downloaded and the speed
Expand All @@ -337,16 +346,17 @@ func (dp *downloadProgressReporter) ReportFailed(err error) {
dp.sourceURI, units.HumanSize(downloaded), units.HumanSize(bytesPerSecond), err,
}
}
dp.log.Infof(msg, args...)
dp.log.Errorf(msg, args...)
if timePast >= dp.warnTimeout {
// see reason in `Report`
dp.log.Warnf(msg, args...)
}
}

// progressLogger is a logger that only needs to implement Infof and Warnf, as those are the only functions
// that the downloadProgressReporter uses.
// progressLogger is a logger that only needs to implement Infof and Warnf and
// Errorf as those are the only functions that the downloadProgressReporter uses.
type progressLogger interface {
Infof(format string, args ...interface{})
Warnf(format string, args ...interface{})
Errorf(format string, args ...interface{})
}
4 changes: 2 additions & 2 deletions internal/pkg/agent/cmd/enroll_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,9 @@ func (c *enrollCmd) Execute(ctx context.Context, streams *cli.IOStreams) error {

defer func() {
if err != nil {
fmt.Fprintln(streams.Out, "Successfully enrolled the Elastic Agent.")
} else {
fmt.Fprintf(streams.Out, "Something went wrong while enrolling the Elastic Agent: %v\n", err)
} else {
fmt.Fprintln(streams.Out, "Successfully enrolled the Elastic Agent.")
}
}()

Expand Down
30 changes: 15 additions & 15 deletions internal/pkg/agent/cmd/logs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ func TestGetLogFilenames(t *testing.T) {
t.Run("returns the correct sorted filelist", func(t *testing.T) {
dir := t.TempDir()

createFileEmpty(t, dir, file2)
createFileEmpty(t, dir, file)
createFileEmpty(t, dir, file1)
createFileEmpty(t, dir, file3)
createFileForTest(t, dir, file2)
createFileForTest(t, dir, file)
createFileForTest(t, dir, file1)
createFileForTest(t, dir, file3)

names, err := getLogFilenames(dir)
require.NoError(t, err)
Expand All @@ -62,14 +62,14 @@ func TestGetLogFilenames(t *testing.T) {
prevDayFile2 := "elastic-agent-20230529-2.ndjson"
prevDayFile3 := "elastic-agent-20230529-3.ndjson"

createFileEmpty(t, dir, file2)
createFileEmpty(t, dir, file)
createFileEmpty(t, dir, prevDayFile1)
createFileEmpty(t, dir, file1)
createFileEmpty(t, dir, prevDayFile)
createFileEmpty(t, dir, prevDayFile2)
createFileEmpty(t, dir, file3)
createFileEmpty(t, dir, prevDayFile3)
createFileForTest(t, dir, file2)
createFileForTest(t, dir, file)
createFileForTest(t, dir, prevDayFile1)
createFileForTest(t, dir, file1)
createFileForTest(t, dir, prevDayFile)
createFileForTest(t, dir, prevDayFile2)
createFileForTest(t, dir, file3)
createFileForTest(t, dir, prevDayFile3)

names, err := getLogFilenames(dir)
require.NoError(t, err)
Expand Down Expand Up @@ -99,7 +99,7 @@ func TestGetLogFilenames(t *testing.T) {

t.Run("does not return non-log entries", func(t *testing.T) {
dir := t.TempDir()
createFileEmpty(t, dir, "excluded")
createFileForTest(t, dir, "excluded")

names, err := getLogFilenames(dir)
require.NoError(t, err)
Expand All @@ -109,7 +109,7 @@ func TestGetLogFilenames(t *testing.T) {

t.Run("returns a list of one", func(t *testing.T) {
dir := t.TempDir()
createFileEmpty(t, dir, file1)
createFileForTest(t, dir, file1)

names, err := getLogFilenames(dir)
require.NoError(t, err)
Expand Down Expand Up @@ -539,7 +539,7 @@ func generateLines(prefix string, start, end int) string {
return b.String()
}

func createFileEmpty(t *testing.T, dir, name string) {
func createFileForTest(t *testing.T, dir, name string) {
createFileContent(t, dir, name, nil)
}

Expand Down
16 changes: 11 additions & 5 deletions pkg/testing/fixture_install.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,12 @@ func (i InstallOpts) toCmdArgs() []string {
return args
}

// Install installs the prepared Elastic Agent binary and returns:
// - the combined output of stdout and stderr
// Install installs the prepared Elastic Agent binary and registers a t.Cleanup
// function to uninstall the agent if it hasn't been uninstalled. It also takes
// cate of collecting a diagnostics when AGENT_COLLECT_DIAG=true or the test
// has failed.
// It returns:
// - the combined output of Install command stdout and stderr
// - an error if any.
func (f *Fixture) Install(ctx context.Context, installOpts *InstallOpts, opts ...process.CmdOption) ([]byte, error) {
installArgs := []string{"install"}
Expand Down Expand Up @@ -114,7 +118,7 @@ func (f *Fixture) Install(ctx context.Context, installOpts *InstallOpts, opts ..
}

// diagnostics is collected when either the environment variable
// AGENT_KEEP_INSTALLED=true or the test is marked failed
// AGENT_COLLECT_DIAG=true or the test is marked failed
collect := collectDiag()
failed := f.t.Failed()
if collect || failed {
Expand Down Expand Up @@ -221,11 +225,13 @@ func (f *Fixture) collectDiagnostics() {
f.t.Logf("failed to collect diagnostics; failed to create %s: %s", diagPath, err)
return
}
outputPath := filepath.Join(diagPath, fmt.Sprintf("%s-diagnostics-%s.zip", f.t.Name(), time.Now().Format(time.RFC3339)))
outputPath := filepath.Join(diagPath,
fmt.Sprintf("%s-diagnostics-%s.zip", f.t.Name(), time.Now().Format(time.RFC3339)))

output, err := f.Exec(ctx, []string{"diagnostics", "-f", outputPath})
if err != nil {
f.t.Logf("failed to collect diagnostics to %s (%s): %s", outputPath, err, output)
f.t.Logf("failed to collect diagnostics to %s. err: %v: output: %q",
outputPath, err, output)
}
}

Expand Down
5 changes: 4 additions & 1 deletion pkg/testing/multipass/provisioner.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,10 @@ func (p *provisioner) launch(ctx context.Context, cfg runner.Config, batch runne

var output bytes.Buffer
p.logger.Logf("Launching multipass image %s", batch.ID)
proc, err := process.Start("multipass", process.WithContext(ctx), process.WithArgs(args), process.WithCmdOptions(runner.AttachOut(&output), runner.AttachErr(&output)))
proc, err := process.Start("multipass",
process.WithContext(ctx),
process.WithArgs(args),
process.WithCmdOptions(runner.AttachOut(&output), runner.AttachErr(&output)))
if err != nil {
return fmt.Errorf("failed to run multipass launch: %w", err)
}
Expand Down
134 changes: 12 additions & 122 deletions pkg/testing/tools/agents.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,137 +6,27 @@ package tools

import (
"context"
"errors"
"fmt"
"os"
"testing"
"time"

"github.com/stretchr/testify/require"
"github.com/stretchr/testify/assert"

"github.com/elastic/elastic-agent-libs/kibana"
"github.com/elastic/elastic-agent/pkg/control/v2/client"
"github.com/elastic/elastic-agent/pkg/control/v2/cproto"
)

// GetAgentByHostnameFromList get an agent by the local_metadata.host.name property, reading from the agents list
func GetAgentByHostnameFromList(client *kibana.Client, hostname string) (*kibana.AgentExisting, error) {
listAgentsResp, err := client.ListAgents(context.Background(), kibana.ListAgentsRequest{})
if err != nil {
return nil, err
// WaitForLocalAgentHealthy will keep checking the agent state until it becomes healthy
// ot the timeout is exceeded. If the agent becomes health, it returns true, if
// not the test is marked as failed and false is returned.
// The timeout is the context deadline, if defined, or set to 2 minutes.
func WaitForLocalAgentHealthy(ctx context.Context, t *testing.T, c client.Client) bool {
// https://github.com/elastic/elastic-agent/pull/3265
timeout := 2 * time.Minute
if deadline, ok := ctx.Deadline(); ok {
timeout = time.Until(deadline)
}

for _, item := range listAgentsResp.Items {
agentHostname := item.LocalMetadata.Host.Hostname
if agentHostname == hostname {
return &item, nil
}
}

return nil, fmt.Errorf("unable to find agent with hostname [%s]", hostname)
}

func GetAgentStatus(client *kibana.Client) (string, error) {
hostname, err := os.Hostname()
if err != nil {
return "", err
}

agent, err := GetAgentByHostnameFromList(client, hostname)
if err != nil {
return "", err
}

return agent.Status, nil
}

func GetAgentVersion(client *kibana.Client) (string, error) {
hostname, err := os.Hostname()
if err != nil {
return "", err
}

agent, err := GetAgentByHostnameFromList(client, hostname)
if err != nil {
return "", err
}

return agent.Agent.Version, err
}

func UnEnrollAgent(client *kibana.Client) error {
hostname, err := os.Hostname()
if err != nil {
return err
}
agentID, err := GetAgentIDByHostname(client, hostname)
if err != nil {
return err
}

unEnrollAgentReq := kibana.UnEnrollAgentRequest{
ID: agentID,
Revoke: true,
}
_, err = client.UnEnrollAgent(context.Background(), unEnrollAgentReq)
if err != nil {
return fmt.Errorf("unable to unenroll agent with ID [%s]: %w", agentID, err)
}

return nil
}

func GetAgentIDByHostname(client *kibana.Client, hostname string) (string, error) {
agent, err := GetAgentByHostnameFromList(client, hostname)
if err != nil {
return "", err
}
return agent.Agent.ID, nil
}

func UpgradeAgent(client *kibana.Client, version string) error {
hostname, err := os.Hostname()
if err != nil {
return err
}
agentID, err := GetAgentIDByHostname(client, hostname)
if err != nil {
return err
}

upgradeAgentReq := kibana.UpgradeAgentRequest{
ID: agentID,
Version: version,
}
_, err = client.UpgradeAgent(context.Background(), upgradeAgentReq)
if err != nil {
return fmt.Errorf("unable to upgrade agent with ID [%s]: %w", agentID, err)
}

return nil
}

func GetDefaultFleetServerURL(client *kibana.Client) (string, error) {
req := kibana.ListFleetServerHostsRequest{}
resp, err := client.ListFleetServerHosts(context.Background(), req)
if err != nil {
return "", fmt.Errorf("unable to list fleet server hosts: %w", err)
}

for _, item := range resp.Items {
if item.IsDefault {
hostURLs := item.HostURLs
if len(hostURLs) > 0 {
return hostURLs[0], nil
}
}
}

return "", errors.New("unable to determine default fleet server host")
}

func WaitForAgent(ctx context.Context, t *testing.T, c client.Client) {
require.Eventually(t, func() bool {
return assert.Eventually(t, func() bool {
err := c.Connect(ctx)
if err != nil {
t.Logf("connecting client to agent: %v", err)
Expand All @@ -150,5 +40,5 @@ func WaitForAgent(ctx context.Context, t *testing.T, c client.Client) {
}
t.Logf("agent state: %+v", state)
return state.State == cproto.State_HEALTHY
}, 2*time.Minute, 10*time.Second, "Agent never became healthy")
}, timeout, 10*time.Second, "Agent never became healthy")
}
26 changes: 24 additions & 2 deletions pkg/testing/tools/check/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ import (

"github.com/stretchr/testify/assert"

"github.com/elastic/elastic-agent-libs/kibana"
"github.com/elastic/elastic-agent/pkg/control/v2/cproto"
integrationtest "github.com/elastic/elastic-agent/pkg/testing"
"github.com/elastic/elastic-agent/pkg/testing/tools/fleet"
)

// ConnectedToFleet checks if the agent defined in the fixture is connected to
// Fleet Server. It uses assert.Eventually and if it fails the last error will
// be printed. It returns if the agent is connected to Fleet Server or not.
func ConnectedToFleet(t *testing.T, fixture *integrationtest.Fixture) bool {
func ConnectedToFleet(t *testing.T, fixture *integrationtest.Fixture, timeout time.Duration) bool {
t.Helper()

var err error
Expand All @@ -28,7 +30,7 @@ func ConnectedToFleet(t *testing.T, fixture *integrationtest.Fixture) bool {
return agentStatus.FleetState == int(cproto.State_HEALTHY)
}

connected := assert.Eventually(t, assertFn, 5*time.Minute, 5*time.Second,
connected := assert.Eventually(t, assertFn, timeout, 5*time.Second,
"want fleet state %s, got %s. agent status: %v",
cproto.State_HEALTHY, cproto.State(agentStatus.FleetState), agentStatus)

Expand All @@ -39,3 +41,23 @@ func ConnectedToFleet(t *testing.T, fixture *integrationtest.Fixture) bool {

return connected
}

// FleetAgentStatus returns a niladic function that returns true if the agent
// has reached expectedStatus; false otherwise. The returned function is intended
// for use with assert.Eventually or require.Eventually.
func FleetAgentStatus(t *testing.T, client *kibana.Client, expectedStatus string) func() bool {
return func() bool {
currentStatus, err := fleet.AgentStatus(client)
if err != nil {
t.Errorf("unable to determine agent status: %s", err.Error())
return false
}

if currentStatus == expectedStatus {
return true
}

t.Logf("Agent fleet status: %s", currentStatus)
return false
}
}
Loading

0 comments on commit 2b85191

Please sign in to comment.