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