|
9 | 9 | "crypto/tls" |
10 | 10 | "fmt" |
11 | 11 | "io/fs" |
| 12 | + "net/http" |
| 13 | + "net/http/httptest" |
12 | 14 | "os" |
13 | 15 | "path/filepath" |
14 | 16 | "runtime" |
@@ -41,6 +43,7 @@ import ( |
41 | 43 | "github.com/elastic/elastic-agent/pkg/core/logger" |
42 | 44 | "github.com/elastic/elastic-agent/pkg/core/logger/loggertest" |
43 | 45 | agtversion "github.com/elastic/elastic-agent/pkg/version" |
| 46 | + "github.com/elastic/elastic-agent/testing/mocks/internal_/pkg/agent/application/info" |
44 | 47 | mocks "github.com/elastic/elastic-agent/testing/mocks/pkg/control/v2/client" |
45 | 48 | ) |
46 | 49 |
|
@@ -1573,3 +1576,135 @@ func TestCopyRunDirectory(t *testing.T) { |
1573 | 1576 | }) |
1574 | 1577 | } |
1575 | 1578 | } |
| 1579 | + |
| 1580 | +func TestUpgradeDirectoryCopyErrors(t *testing.T) { |
| 1581 | + log, _ := loggertest.New("test") |
| 1582 | + |
| 1583 | + initialVersion := agtversion.NewParsedSemVer(1, 2, 3, "SNAPSHOT", "") |
| 1584 | + initialArtifactName, initialArchiveFiles := buildArchiveFiles(t, archiveFilesWithMoreComponents, initialVersion, "abcdef") |
| 1585 | + |
| 1586 | + targetVersion := agtversion.NewParsedSemVer(3, 4, 5, "SNAPSHOT", "") |
| 1587 | + targetArtifactName, targetArchiveFiles := buildArchiveFiles(t, archiveFilesWithMoreComponents, targetVersion, "ghijkl") |
| 1588 | + |
| 1589 | + mockAgentInfo := info.NewAgent(t) |
| 1590 | + mockAgentInfo.On("Version").Return(targetVersion.String()) |
| 1591 | + |
| 1592 | + upgradeDetails := details.NewDetails(targetVersion.String(), details.StateRequested, "test") |
| 1593 | + |
| 1594 | + tempUnpacker, err := NewUpgrader(log, &artifact.Config{}, mockAgentInfo) // used only to unpack the initial archive |
| 1595 | + require.NoError(t, err) |
| 1596 | + |
| 1597 | + genericError := errors.New("test error") |
| 1598 | + |
| 1599 | + testCases := map[string]struct { |
| 1600 | + mockReturnedError error |
| 1601 | + expectedError error |
| 1602 | + }{ |
| 1603 | + "should return error if run directory copy fails": { |
| 1604 | + mockReturnedError: genericError, |
| 1605 | + expectedError: genericError, |
| 1606 | + }, |
| 1607 | + } |
| 1608 | + |
| 1609 | + for _, te := range upgradeErrors.OS_DiskSpaceErrors { |
| 1610 | + testCases[fmt.Sprintf("should return error if run directory copy fails with disk space error: %v", te)] = struct { |
| 1611 | + mockReturnedError error |
| 1612 | + expectedError error |
| 1613 | + }{ |
| 1614 | + mockReturnedError: te, |
| 1615 | + expectedError: upgradeErrors.ErrInsufficientDiskSpace, |
| 1616 | + } |
| 1617 | + } |
| 1618 | + |
| 1619 | + for _, copiedDir := range []string{"action_store", "run_directory"} { |
| 1620 | + for name, tc := range testCases { |
| 1621 | + t.Run(fmt.Sprintf("when copying %s: %s", copiedDir, name), func(t *testing.T) { |
| 1622 | + baseDir := t.TempDir() |
| 1623 | + paths.SetTop(baseDir) |
| 1624 | + paths.SetDownloads(filepath.Join(baseDir, "downloads")) // ensure the upgrader uses a predictable path for downloads |
| 1625 | + initialArchive, err := createArchive(t, initialArtifactName, initialArchiveFiles) |
| 1626 | + require.NoError(t, err) |
| 1627 | + |
| 1628 | + t.Logf("Created archive: %s", initialArchive) |
| 1629 | + |
| 1630 | + _, err = tempUnpacker.unpack(initialVersion.String(), initialArchive, paths.Data(), "") |
| 1631 | + require.NoError(t, err) |
| 1632 | + |
| 1633 | + // The archive file list does not contain the action store files, so we need to |
| 1634 | + // create them |
| 1635 | + actionStorePaths := []string{paths.AgentActionStoreFile(), paths.AgentStateStoreYmlFile(), paths.AgentStateStoreFile()} |
| 1636 | + for _, path := range actionStorePaths { |
| 1637 | + err := os.MkdirAll(filepath.Dir(path), 0o700) |
| 1638 | + require.NoError(t, err, "error creating directory %s", filepath.Dir(path)) |
| 1639 | + |
| 1640 | + err = os.WriteFile(path, []byte(fmt.Sprintf("initial agent %s content", filepath.Base(path))), 0o600) |
| 1641 | + require.NoError(t, err, "error writing to %s", path) |
| 1642 | + } |
| 1643 | + |
| 1644 | + var createdFilePaths []string |
| 1645 | + if copiedDir == "run_directory" { |
| 1646 | + // Create several files in the initial run path and save their paths in an array. |
| 1647 | + initialRunPath := paths.Run() |
| 1648 | + require.NoError(t, os.MkdirAll(initialRunPath, 0o755)) |
| 1649 | + |
| 1650 | + for i := 0; i < 3; i++ { |
| 1651 | + filePath := filepath.Join(initialRunPath, fmt.Sprintf("file%d.txt", i)) |
| 1652 | + err := os.WriteFile(filePath, []byte(fmt.Sprintf("content for file %d", i)), 0o600) |
| 1653 | + require.NoError(t, err) |
| 1654 | + createdFilePaths = append(createdFilePaths, filePath) |
| 1655 | + } |
| 1656 | + } |
| 1657 | + |
| 1658 | + targetArchive, err := createArchive(t, targetArtifactName, targetArchiveFiles) |
| 1659 | + require.NoError(t, err) |
| 1660 | + |
| 1661 | + t.Logf("Created archive: %s", targetArchive) |
| 1662 | + |
| 1663 | + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 1664 | + http.ServeFile(w, r, targetArchive) |
| 1665 | + })) |
| 1666 | + t.Cleanup(server.Close) |
| 1667 | + |
| 1668 | + mockCalled := false |
| 1669 | + |
| 1670 | + if copiedDir == "run_directory" { |
| 1671 | + tmpCopyRunDirFunc := copyRunDirectoryFunc |
| 1672 | + t.Cleanup(func() { |
| 1673 | + copyRunDirectoryFunc = tmpCopyRunDirFunc |
| 1674 | + }) |
| 1675 | + |
| 1676 | + copyRunDirectoryFunc = func(log *logger.Logger, oldRunPath string, newRunPath string) error { |
| 1677 | + mockCalled = true |
| 1678 | + return tc.mockReturnedError |
| 1679 | + } |
| 1680 | + } else { |
| 1681 | + tmpCopyActionStoreFunc := copyActionStoreFunc |
| 1682 | + t.Cleanup(func() { |
| 1683 | + copyActionStoreFunc = tmpCopyActionStoreFunc |
| 1684 | + }) |
| 1685 | + |
| 1686 | + copyActionStoreFunc = func(log *logger.Logger, newHome string) error { |
| 1687 | + mockCalled = true |
| 1688 | + return tc.mockReturnedError |
| 1689 | + } |
| 1690 | + } |
| 1691 | + |
| 1692 | + config := artifact.Config{ |
| 1693 | + TargetDirectory: paths.Downloads(), |
| 1694 | + SourceURI: server.URL, |
| 1695 | + RetrySleepInitDuration: 1 * time.Second, |
| 1696 | + HTTPTransportSettings: httpcommon.HTTPTransportSettings{ |
| 1697 | + Timeout: 1 * time.Second, |
| 1698 | + }, |
| 1699 | + } |
| 1700 | + |
| 1701 | + upgrader, err := NewUpgrader(log, &config, mockAgentInfo) |
| 1702 | + require.NoError(t, err) |
| 1703 | + |
| 1704 | + _, err = upgrader.Upgrade(context.Background(), targetVersion.String(), server.URL, nil, upgradeDetails, true, true) |
| 1705 | + require.True(t, mockCalled, "mock should be called") |
| 1706 | + require.ErrorIs(t, err, tc.expectedError, "expected error mismatch") |
| 1707 | + }) |
| 1708 | + } |
| 1709 | + } |
| 1710 | +} |
0 commit comments