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