55package upgrade
66
77import (
8+ "bytes"
89 "context"
910 "crypto/tls"
1011 "fmt"
12+ "io"
13+ "net/http"
14+ "net/http/httptest"
1115 "os"
1216 "path/filepath"
1317 "runtime"
@@ -26,6 +30,8 @@ import (
2630 "github.com/elastic/elastic-agent-libs/transport/tlscommon"
2731 "github.com/elastic/elastic-agent/internal/pkg/agent/application/paths"
2832 "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/artifact"
33+ downloadErrors "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/artifact/download/errors"
34+ "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/common"
2935 "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/details"
3036 "github.com/elastic/elastic-agent/internal/pkg/agent/errors"
3137 "github.com/elastic/elastic-agent/internal/pkg/config"
@@ -38,6 +44,7 @@ import (
3844 "github.com/elastic/elastic-agent/pkg/core/logger"
3945 "github.com/elastic/elastic-agent/pkg/core/logger/loggertest"
4046 agtversion "github.com/elastic/elastic-agent/pkg/version"
47+ "github.com/elastic/elastic-agent/testing/mocks/internal_/pkg/agent/application/info"
4148 mocks "github.com/elastic/elastic-agent/testing/mocks/pkg/control/v2/client"
4249)
4350
@@ -1365,3 +1372,131 @@ func createArchive(t *testing.T, archiveName string, archiveFiles []files) (stri
13651372 }
13661373 return createTarArchive (t , archiveName , archiveFiles )
13671374}
1375+
1376+ func TestUpgradeUnpackErrors (t * testing.T ) {
1377+ log , _ := loggertest .New ("test" )
1378+
1379+ targetVersion := agtversion .NewParsedSemVer (3 , 4 , 5 , "SNAPSHOT" , "" )
1380+ targetArtifactName , targetArchiveFiles := buildArchiveFiles (t , archiveFilesWithMoreComponents , targetVersion , "ghijkl" )
1381+
1382+ // Get the content of the component file to be used in copy function
1383+ // assertions. It will be used to make sure the error gets triggered in the
1384+ // copy call in the unpacker and not the downloader.
1385+ targetArchiveComponentsFileContent := ""
1386+ for _ , file := range targetArchiveFiles {
1387+ if file .fType == REGULAR {
1388+ dir := filepath .Dir (file .path )
1389+ if strings .HasSuffix (dir , "components" ) {
1390+ targetArchiveComponentsFileContent = file .content
1391+ break
1392+ }
1393+ }
1394+ }
1395+
1396+ // dir name of the versioned home
1397+ // used to calculate component paths
1398+ newVersionedDirName := "elastic-agent-3.4.5-SNAPSHOT-ghijkl"
1399+
1400+ mockAgentInfo := info .NewAgent (t )
1401+ mockAgentInfo .On ("Version" ).Return (targetVersion .String ())
1402+
1403+ upgradeDetails := details .NewDetails (targetVersion .String (), details .StateRequested , "test" )
1404+
1405+ mockStdlibFuncs := []common.MockStdLibFuncName {common .CopyFuncName , common .OpenFileFuncName , common .MkdirAllFuncName }
1406+
1407+ testCases := map [string ]struct {
1408+ mockStdlibFunc common.MockStdLibFuncName
1409+ mockReturnedError error
1410+ expectedError error
1411+ }{}
1412+
1413+ for _ , mockStdlibFunc := range mockStdlibFuncs {
1414+ for _ , te := range downloadErrors .OS_DiskSpaceErrors {
1415+ testCases [fmt .Sprintf ("unpack_should_return_error_if_unpack_%s_fails: %v" , mockStdlibFunc , te )] = struct {
1416+ mockStdlibFunc common.MockStdLibFuncName
1417+ mockReturnedError error
1418+ expectedError error
1419+ }{
1420+ mockStdlibFunc : mockStdlibFunc ,
1421+ mockReturnedError : te ,
1422+ expectedError : downloadErrors .ErrInsufficientDiskSpace ,
1423+ }
1424+ }
1425+ }
1426+
1427+ for name , tc := range testCases {
1428+ t .Run (name , func (t * testing.T ) {
1429+ tempDir := t .TempDir ()
1430+ paths .SetTop (tempDir )
1431+ paths .SetDownloads (filepath .Join (tempDir , "downloads" ))
1432+
1433+ targetArchive , err := createArchive (t , targetArtifactName , targetArchiveFiles )
1434+ require .NoError (t , err )
1435+
1436+ // Used to assert that the mkdirAll error is triggered in the
1437+ // unpacker and not the downloader
1438+ newComponentsDir := filepath .Join (paths .Data (), newVersionedDirName , "components" )
1439+ // Used to assert that the openFile error is triggered in the
1440+ // unpacker and not the downloader
1441+ newComponentsFile := filepath .Join (newComponentsDir , "comp1" )
1442+
1443+ t .Logf ("Created archive: %s" , targetArchive )
1444+
1445+ server := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
1446+ http .ServeFile (w , r , targetArchive )
1447+ }))
1448+ t .Cleanup (server .Close )
1449+
1450+ config := artifact.Config {
1451+ TargetDirectory : paths .Downloads (),
1452+ SourceURI : server .URL ,
1453+ RetrySleepInitDuration : 1 * time .Second ,
1454+ HTTPTransportSettings : httpcommon.HTTPTransportSettings {
1455+ Timeout : 1 * time .Second ,
1456+ },
1457+ }
1458+
1459+ stdlibMocker := common .PrepareStdLibMocks (common.StdLibMocks {
1460+ CopyMock : func (dst io.Writer , src io.Reader ) (int64 , error ) {
1461+ // If the content is not the same as the target archive components file content,
1462+ // write the content to the destination. This is to make
1463+ // sure that the error is triggered in unpacker and not in downloader
1464+ buf , err := io .ReadAll (src )
1465+ require .NoError (t , err )
1466+ if ! bytes .Equal (buf , []byte (targetArchiveComponentsFileContent )) {
1467+ return io .Copy (dst , bytes .NewReader (buf ))
1468+ }
1469+
1470+ return 0 , tc .mockReturnedError
1471+ },
1472+ OpenFileMock : func (name string , flag int , perm os.FileMode ) (* os.File , error ) {
1473+ // Make sure that the openFile error is triggered in the
1474+ // unpacker and not the downloader
1475+ if name != newComponentsFile {
1476+ return os .OpenFile (name , flag , perm )
1477+ }
1478+
1479+ return nil , tc .mockReturnedError
1480+ },
1481+ MkdirAllMock : func (path string , perm os.FileMode ) error {
1482+ // Make sure that the mkdirAll error is triggered in the
1483+ // unpacker and not the downloader
1484+ if path != newComponentsDir {
1485+ return os .MkdirAll (path , perm )
1486+ }
1487+
1488+ return tc .mockReturnedError
1489+ },
1490+ })
1491+
1492+ stdlibMocker (t , tc .mockStdlibFunc )
1493+
1494+ upgrader , err := NewUpgrader (log , & config , mockAgentInfo )
1495+ require .NoError (t , err )
1496+
1497+ _ , err = upgrader .Upgrade (context .Background (), targetVersion .String (), server .URL , nil , upgradeDetails , true , true )
1498+ require .ErrorIs (t , err , tc .expectedError , "expected error mismatch" )
1499+ require .Equal (t , upgradeDetails .State , details .StateExtracting )
1500+ })
1501+ }
1502+ }
0 commit comments